Re: [PATCH v2 3/7] migration/multifd: Zero page transmission on the multifd thread.

2024-02-16 Thread Richard Henderson

On 2/16/24 12:39, Hao Xiang wrote:

+void multifd_zero_page_check_recv(MultiFDRecvParams *p)
+{
+for (int i = 0; i < p->zero_num; i++) {
+void *page = p->host + p->zero[i];
+if (!buffer_is_zero(page, p->page_size)) {
+memset(page, 0, p->page_size);
+}
+}
+}


You should not check the buffer is zero here, you should just zero it.


r~



Re: [PATCH] tests/cdrom-test: Add cdrom test for LoongArch virt machine

2024-02-16 Thread maobibo




On 2024/2/6 下午5:20, Thomas Huth wrote:

On 06/02/2024 03.29, maobibo wrote:

Hi Philippe,

On 2024/2/5 下午8:58, Philippe Mathieu-Daudé wrote:

Hi Bibo,

On 5/2/24 03:13, Bibo Mao wrote:

The cdrom test skips to execute on LoongArch system with command
"make check", this patch enables cdrom test for LoongArch virt
machine platform.

With this patch, cdrom test passes to run on LoongArch virt
machine type.

Signed-off-by: Bibo Mao 
---
  tests/qtest/cdrom-test.c | 3 +++
  1 file changed, 3 insertions(+)

diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c
index 0945383789..c8b97d8d9a 100644
--- a/tests/qtest/cdrom-test.c
+++ b/tests/qtest/cdrom-test.c
@@ -271,6 +271,9 @@ int main(int argc, char **argv)
  const char *virtmachine[] = { "virt", NULL };
  add_cdrom_param_tests(virtmachine);
  }
+    } else if (g_str_equal(arch, "loongarch64")) {
+    const char *virtmachine[] = { "virt", NULL };
+    add_cdrom_param_tests(virtmachine);


What is the default device used, virtio-blk-pci?


yes, it is. For virt machine type, the default type for block device is
virtio interface, and it is defined at function loongarch_class_init().
    mc->block_default_type = IF_VIRTIO


Ok, then you might need to check whether your patch still works when you 
run "configure" with "--without-default-devices". You might need to 
check with 'if (qtest_has_device("virtio-blk-pci"))' whether the device 
is really available in the binary, like it is done some lines earlier in 
the arm case.
Sorry for the late response. Yes cdrom test case will report failure 
with it is compiled with --without-default-devices option.


I will refresh the patch like arm case.

Regards
Bibo Mao


  Thomas






Re: [PATCH] i386: load kernel on xen using DMA

2024-02-16 Thread Marek Marczykowski-Górecki
On Fri, Jun 18, 2021 at 09:54:14AM +0100, Alex Bennée wrote:
> 
> Marek Marczykowski-Górecki  writes:
> 
> > Kernel on Xen is loaded via fw_cfg. Previously it used non-DMA version,
> > which loaded the kernel (and initramfs) byte by byte. Change this
> > to DMA, to load in bigger chunks.
> > This change alone reduces load time of a (big) kernel+initramfs from
> > ~10s down to below 1s.
> >
> > This change was suggested initially here:
> > https://lore.kernel.org/xen-devel/20180216204031.5...@gmail.com/
> > Apparently this alone is already enough to get massive speedup.
> >
> > Signed-off-by: Marek Marczykowski-Górecki 
> > ---
> >  hw/i386/pc.c | 3 ++-
> >  1 file changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> > index 8a84b25a03..14e43d4da4 100644
> > --- a/hw/i386/pc.c
> > +++ b/hw/i386/pc.c
> > @@ -839,7 +839,8 @@ void xen_load_linux(PCMachineState *pcms)
> >  
> >  assert(MACHINE(pcms)->kernel_filename != NULL);
> >  
> > -fw_cfg = fw_cfg_init_io(FW_CFG_IO_BASE);
> > +fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4,
> > +_space_memory);
> >  fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
> >  rom_set_fw(fw_cfg);
> 
> Gentle ping. The fix looks perfectly sane to me but I don't have any x86
> Xen HW to test this one. Are the x86 maintainers happy to take this on?

Ping...

> 
> FWIW:
> 
> Reviewed-by: Alex Bennée 
> 
> -- 
> Alex Bennée
> 

-- 
Best Regards,
Marek Marczykowski-Górecki
Invisible Things Lab


signature.asc
Description: PGP signature


Re: [PATCH v2 3/3] target/riscv/translate.c: set vstart_eq_zero in mark_vs_dirty()

2024-02-16 Thread Daniel Henrique Barboza




On 2/16/24 20:41, Richard Henderson wrote:

On 2/16/24 12:40, Daniel Henrique Barboza wrote:

After reading the reviews of patches 1 and 3 what I'm considering here is:

1 - drop patch 1;


Ok.


2 - there's a patch from Ivan Klokov sent 2 months ago:

"[PATCH 1/1] target/riscv: Clear vstart_qe_zero flag"
https://lore.kernel.org/qemu-riscv/20231214111851.142532-1-ivan.klo...@syntacore.com/

His patch is closer to what you suggested than mine. He already renamed 
mark_vs_dirty()
to finalize_rvv_inst() and made it set start_eq_zero unconditionally. It needs a
little work (i.e. remove the ifds from the function) that I'll do myself.

3 - I'll keep patch 2 to reduce the redundant calls to the now 
finalize_rvv_inst();


Ok.


4 - Add another patch to through all "gen_set_label(over)" cond branches and set
vstart = 0 && vstart_eq_zero manually when we're doing the jump.

In fact, shouldn't we return earlier if we're not taking the branch? Otherwise
we'll set vstart twice in case we didn't get the branch. E.g:

   TCGLabel *over = gen_new_label();
   tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
   (...)
   finalize_rvv_insn();
   return true;

   gen_set_label(over);
   /* some TCG ops to set cpu_vstart to zero. Perhaps a helper?  */
   s->vstart_eq_zero = true;
   return true;


That will break, of course, because you wouldn't emit 'over'.
You really need to get translation-time and run-time separate in your head.

That said, I think these brcond(vstart >= vl) are a mistake.
The loops within the helpers are generally of the form

     for (i = env->vstart; i < evl; i++, env->vstart++) {

which will operate just fine with vstart >= vl, iterating zero times.
We will then fall through to the post-insn cleanup,

     env->vstart = 0;
     vext_set_tail_elems_1s(evl, vd, desc, nf, esz, max_elems);

or whatever.

I would expect the condition vstart >= vl to never happen in practice.  I believe the 
only way to induce it is an explicit write to vstart.  Therefore I think you should not 
attempt to "optimize away" the call to the helper.

Of course you will want to double-check all of the loop iterations in the 
associated helpers when removing the branches.


Ok!




r~


PS: I believe a better form for the ldst loops is

     for (i = env->vstart; i < evl; env->vstart = ++i)

to avoid re-reading from vstart each iteration.



I'll add a patch to convert these loops. Thanks,


Daniel




[PATCH v2] target: hppa: Fix unaligned double word accesses for hppa64

2024-02-16 Thread Guenter Roeck
Unaligned 64-bit accesses were found in Linux to clobber carry bits,
resulting in bad results if an arithmetic operation involving a
carry bit was executed after an unaligned 64-bit operation.

hppa 2.0 defines additional carry bits in PSW register bits 32..39.
When restoring PSW after executing an unaligned instruction trap, those
bits were not cleared and ended up to be active all the time. Since there
are no bits other than the upper carry bits needed in the upper 32 bit of
env->psw and since those are stored in env->psw_cb, just clear the entire
upper 32 bit when storing psw to solve the problem unconditionally.

Fixes: 931adff31478 ("target/hppa: Update cpu_hppa_get/put_psw for hppa64")
Cc: Richard Henderson 
Cc: Charlie Jenkins 
Cc: Helge Deller 
Reviewed-by: Richard Henderson 
Signed-off-by: Guenter Roeck 
---
v2: Rework to not require conditional code [Richard]
Add Richard's Reviewed-by: tag

 target/hppa/helper.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/target/hppa/helper.c b/target/hppa/helper.c
index 859644c47a..9d217d051c 100644
--- a/target/hppa/helper.c
+++ b/target/hppa/helper.c
@@ -76,7 +76,8 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw)
 }
 psw &= ~reserved;
 
-env->psw = psw & ~(PSW_N | PSW_V | PSW_CB);
+env->psw = psw & (uint32_t)~(PSW_N | PSW_V | PSW_CB);
+
 env->psw_n = (psw / PSW_N) & 1;
 env->psw_v = -((psw / PSW_V) & 1);
 
-- 
2.39.2




Re: [PATCH v3 1/3] hw/i2c: core: Add reset

2024-02-16 Thread Corey Minyard
On Thu, Feb 08, 2024 at 04:39:10PM +, Peter Maydell wrote:
> On Fri, 2 Feb 2024 at 20:48, Joe Komlodi  wrote:
> >
> > It's possible for a reset to come in the middle of a transaction, which
> > causes the bus to be in an old state when a new transaction comes in.
> >
> > Signed-off-by: Joe Komlodi 
> > ---
> >  hw/i2c/core.c| 19 +++
> >  include/hw/i2c/i2c.h |  2 +-
> >  2 files changed, 20 insertions(+), 1 deletion(-)
> >
> > diff --git a/hw/i2c/core.c b/hw/i2c/core.c
> > index 4cf30b2c86..3128067bba 100644
> > --- a/hw/i2c/core.c
> > +++ b/hw/i2c/core.c
> > @@ -23,10 +23,29 @@ static Property i2c_props[] = {
> >  DEFINE_PROP_END_OF_LIST(),
> >  };
> >
> > +static void i2c_bus_hold_reset(Object *obj)
> > +{
> > +I2CBus *bus = I2C_BUS(obj);
> > +I2CNode *node, *next;
> > +
> > +bus->broadcast = false;
> > +QLIST_FOREACH_SAFE(node, >current_devs, next, next) {
> > +QLIST_REMOVE(node, next);
> > +g_free(node);
> > +}
> > +}
> 
> This does what it says it's going to do; but I think it
> would be good to hear from Corey whether it's better to
> do this, or instead to call i2c_end_transfer() in the
> reset-enter phase.

Sorry, I missed this, I'm having major chaos going on right now in my
life.

I don't think i2c_end_transfer() is the right thing to do.  The transfer
has not cleanly ended, it is just forgotten.

> 
> Mostly QEMU's "reset" is like power-cycling, in which case
> I guess that what we have here where we just forget about
> the in-progress transfer and assume the device on the other
> end is also going to reset back to a neutral state is what
> we want.
> 
> Does i2c have a concept of a bus-level "reset" operation?

No, it does not.  Most I2C devices don't even have a reset pin.  In a
reset situation in real hardware, the operation would be aborted by
the lines drifting high after the bus master has been reset.

So I think this is fine as is.

-corey

> 
> > +
> > +static void i2c_bus_class_init(ObjectClass *klass, void *data)
> > +{
> > +ResettableClass *rc = RESETTABLE_CLASS(klass);
> > +rc->phases.hold = i2c_bus_hold_reset;
> > +}
> > +
> >  static const TypeInfo i2c_bus_info = {
> >  .name = TYPE_I2C_BUS,
> >  .parent = TYPE_BUS,
> >  .instance_size = sizeof(I2CBus),
> > +.class_init = i2c_bus_class_init,
> >  };
> 
> 
> 
> >  static int i2c_bus_pre_save(void *opaque)
> > diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h
> > index 2a3abacd1b..49580e30e2 100644
> > --- a/include/hw/i2c/i2c.h
> > +++ b/include/hw/i2c/i2c.h
> > @@ -64,7 +64,7 @@ struct I2CSlave {
> >  };
> >
> >  #define TYPE_I2C_BUS "i2c-bus"
> > -OBJECT_DECLARE_SIMPLE_TYPE(I2CBus, I2C_BUS)
> > +OBJECT_DECLARE_TYPE(I2CBus, I2CBusClass, I2C_BUS)
> 
> I don't think you need this change any more ?
> 
> thanks
> -- PMM
> 



[PATCH v5 00/10] Optimize buffer_is_zero

2024-02-16 Thread Richard Henderson
v3: https://patchew.org/QEMU/20240206204809.9859-1-amona...@ispras.ru/
v4: 
https://patchew.org/QEMU/20240215081449.848220-1-richard.hender...@linaro.org/

Changes for v5:
  - Move 3 byte sample back inline; document it.
  - Drop AArch64 SVE alternative; neoverse-v2 still recommends simd for memcpy.
  - Use UMAXV for aarch64 simd reduction
3 cycles on cortex-a76, 2 cycles on neoverse-n1,
as compared to UQXTN or CMEQ+SHRN at 4 cycles each.
  - Add benchmark of zeros.

The benchmark is trivial, and could be improved so that it
prints the name of the acceleration routine instead of its
index in the selection process.  But its is good enough to
see that #0 is faster than #1, etc.

A sample set:

Apple M1:
  buffer_is_zero #0: 135416.27 MB/sec
  buffer_is_zero #1: 111771.25 MB/sec

Neoverse N1:
  buffer_is_zero #0: 56489.82 MB/sec
  buffer_is_zero #1: 36347.93 MB/sec

i7-1195G7:
  buffer_is_zero #0: 137327.40 MB/sec
  buffer_is_zero #1: 69159.20 MB/sec
  buffer_is_zero #2: 38319.80 MB/sec


r~


Alexander Monakov (5):
  util/bufferiszero: Remove SSE4.1 variant
  util/bufferiszero: Remove AVX512 variant
  util/bufferiszero: Reorganize for early test for acceleration
  util/bufferiszero: Remove useless prefetches
  util/bufferiszero: Optimize SSE2 and AVX2 variants

Richard Henderson (5):
  util/bufferiszero: Improve scalar variant
  util/bufferiszero: Introduce biz_accel_fn typedef
  util/bufferiszero: Simplify test_buffer_is_zero_next_accel
  util/bufferiszero: Add simd acceleration for aarch64
  tests/bench: Add bufferiszero-bench

 include/qemu/cutils.h|  32 ++-
 tests/bench/bufferiszero-bench.c |  42 +++
 util/bufferiszero.c  | 449 +--
 tests/bench/meson.build  |   4 +-
 4 files changed, 319 insertions(+), 208 deletions(-)
 create mode 100644 tests/bench/bufferiszero-bench.c

-- 
2.34.1




[PATCH v5 05/10] util/bufferiszero: Optimize SSE2 and AVX2 variants

2024-02-16 Thread Richard Henderson
From: Alexander Monakov 

Increase unroll factor in SIMD loops from 4x to 8x in order to move
their bottlenecks from ALU port contention to load issue rate (two loads
per cycle on popular x86 implementations).

Avoid using out-of-bounds pointers in loop boundary conditions.

Follow SSE2 implementation strategy in the AVX2 variant. Avoid use of
PTEST, which is not profitable there (like in the removed SSE4 variant).

Signed-off-by: Alexander Monakov 
Signed-off-by: Mikhail Romanov 
Reviewed-by: Richard Henderson 
Message-Id: <20240206204809.9859-6-amona...@ispras.ru>
---
 util/bufferiszero.c | 111 +---
 1 file changed, 73 insertions(+), 38 deletions(-)

diff --git a/util/bufferiszero.c b/util/bufferiszero.c
index 00118d649e..02df82b4ff 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -67,62 +67,97 @@ static bool buffer_is_zero_integer(const void *buf, size_t 
len)
 #if defined(CONFIG_AVX2_OPT) || defined(__SSE2__)
 #include 
 
-/* Note that each of these vectorized functions require len >= 64.  */
+/* Helper for preventing the compiler from reassociating
+   chains of binary vector operations.  */
+#define SSE_REASSOC_BARRIER(vec0, vec1) asm("" : "+x"(vec0), "+x"(vec1))
+
+/* Note that these vectorized functions may assume len >= 256.  */
 
 static bool __attribute__((target("sse2")))
 buffer_zero_sse2(const void *buf, size_t len)
 {
-__m128i t = _mm_loadu_si128(buf);
-__m128i *p = (__m128i *)(((uintptr_t)buf + 5 * 16) & -16);
-__m128i *e = (__m128i *)(((uintptr_t)buf + len) & -16);
-__m128i zero = _mm_setzero_si128();
+/* Unaligned loads at head/tail.  */
+__m128i v = *(__m128i_u *)(buf);
+__m128i w = *(__m128i_u *)(buf + len - 16);
+/* Align head/tail to 16-byte boundaries.  */
+const __m128i *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16);
+const __m128i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16);
+__m128i zero = { 0 };
 
-/* Loop over 16-byte aligned blocks of 64.  */
-while (likely(p <= e)) {
-t = _mm_cmpeq_epi8(t, zero);
-if (unlikely(_mm_movemask_epi8(t) != 0x)) {
+/* Collect a partial block at tail end.  */
+v |= e[-1]; w |= e[-2];
+SSE_REASSOC_BARRIER(v, w);
+v |= e[-3]; w |= e[-4];
+SSE_REASSOC_BARRIER(v, w);
+v |= e[-5]; w |= e[-6];
+SSE_REASSOC_BARRIER(v, w);
+v |= e[-7]; v |= w;
+
+/*
+ * Loop over complete 128-byte blocks.
+ * With the head and tail removed, e - p >= 14, so the loop
+ * must iterate at least once.
+ */
+do {
+v = _mm_cmpeq_epi8(v, zero);
+if (unlikely(_mm_movemask_epi8(v) != 0x)) {
 return false;
 }
-t = p[-4] | p[-3] | p[-2] | p[-1];
-p += 4;
-}
+v = p[0]; w = p[1];
+SSE_REASSOC_BARRIER(v, w);
+v |= p[2]; w |= p[3];
+SSE_REASSOC_BARRIER(v, w);
+v |= p[4]; w |= p[5];
+SSE_REASSOC_BARRIER(v, w);
+v |= p[6]; w |= p[7];
+SSE_REASSOC_BARRIER(v, w);
+v |= w;
+p += 8;
+} while (p < e - 7);
 
-/* Finish the aligned tail.  */
-t |= e[-3];
-t |= e[-2];
-t |= e[-1];
-
-/* Finish the unaligned tail.  */
-t |= _mm_loadu_si128(buf + len - 16);
-
-return _mm_movemask_epi8(_mm_cmpeq_epi8(t, zero)) == 0x;
+return _mm_movemask_epi8(_mm_cmpeq_epi8(v, zero)) == 0x;
 }
 
 #ifdef CONFIG_AVX2_OPT
 static bool __attribute__((target("avx2")))
 buffer_zero_avx2(const void *buf, size_t len)
 {
-/* Begin with an unaligned head of 32 bytes.  */
-__m256i t = _mm256_loadu_si256(buf);
-__m256i *p = (__m256i *)(((uintptr_t)buf + 5 * 32) & -32);
-__m256i *e = (__m256i *)(((uintptr_t)buf + len) & -32);
+/* Unaligned loads at head/tail.  */
+__m256i v = *(__m256i_u *)(buf);
+__m256i w = *(__m256i_u *)(buf + len - 32);
+/* Align head/tail to 32-byte boundaries.  */
+const __m256i *p = QEMU_ALIGN_PTR_DOWN(buf + 32, 32);
+const __m256i *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 32);
+__m256i zero = { 0 };
 
-/* Loop over 32-byte aligned blocks of 128.  */
-while (p <= e) {
-if (unlikely(!_mm256_testz_si256(t, t))) {
+/* Collect a partial block at tail end.  */
+v |= e[-1]; w |= e[-2];
+SSE_REASSOC_BARRIER(v, w);
+v |= e[-3]; w |= e[-4];
+SSE_REASSOC_BARRIER(v, w);
+v |= e[-5]; w |= e[-6];
+SSE_REASSOC_BARRIER(v, w);
+v |= e[-7]; v |= w;
+
+/* Loop over complete 256-byte blocks.  */
+for (; p < e - 7; p += 8) {
+/* PTEST is not profitable here.  */
+v = _mm256_cmpeq_epi8(v, zero);
+if (unlikely(_mm256_movemask_epi8(v) != 0x)) {
 return false;
 }
-t = p[-4] | p[-3] | p[-2] | p[-1];
-p += 4;
-} ;
+v = p[0]; w = p[1];
+SSE_REASSOC_BARRIER(v, w);
+v |= p[2]; w |= p[3];
+SSE_REASSOC_BARRIER(v, w);
+v |= p[4]; w |= p[5];
+SSE_REASSOC_BARRIER(v, w);
+

[PATCH v5 10/10] tests/bench: Add bufferiszero-bench

2024-02-16 Thread Richard Henderson
Benchmark each acceleration function vs an aligned buffer of zeros.

Signed-off-by: Richard Henderson 
---
 tests/bench/bufferiszero-bench.c | 42 
 tests/bench/meson.build  |  4 ++-
 2 files changed, 45 insertions(+), 1 deletion(-)
 create mode 100644 tests/bench/bufferiszero-bench.c

diff --git a/tests/bench/bufferiszero-bench.c b/tests/bench/bufferiszero-bench.c
new file mode 100644
index 00..1fa2eb6973
--- /dev/null
+++ b/tests/bench/bufferiszero-bench.c
@@ -0,0 +1,42 @@
+/*
+ * QEMU buffer_is_zero speed benchmark
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/units.h"
+
+static void test(const void *opaque)
+{
+size_t len = 64 * KiB;
+void *buf = g_malloc0(len);
+int accel_index = 0;
+
+do {
+double total = 0.0;
+
+g_test_timer_start();
+do {
+buffer_is_zero_ge256(buf, len);
+total += len;
+} while (g_test_timer_elapsed() < 5.0);
+
+total /= MiB;
+g_test_message("buffer_is_zero #%d: %.2f MB/sec",
+   accel_index, total / g_test_timer_last());
+
+accel_index++;
+} while (test_buffer_is_zero_next_accel());
+
+g_free(buf);
+}
+
+int main(int argc, char **argv)
+{
+g_test_init(, , NULL);
+g_test_add_data_func("/cutils/bufferiszero/speed", NULL, test);
+return g_test_run();
+}
diff --git a/tests/bench/meson.build b/tests/bench/meson.build
index 7e76338a52..70d45ff400 100644
--- a/tests/bench/meson.build
+++ b/tests/bench/meson.build
@@ -17,7 +17,9 @@ executable('atomic64-bench',
dependencies: [qemuutil],
build_by_default: false)
 
-benchs = {}
+benchs = {
+'bufferiszero-bench': [],
+}
 
 if have_block
   benchs += {
-- 
2.34.1




[PATCH v5 03/10] util/bufferiszero: Reorganize for early test for acceleration

2024-02-16 Thread Richard Henderson
From: Alexander Monakov 

Test for length >= 256 inline, where is is often a constant.
Before calling into the accelerated routine, sample three bytes
from the buffer, which handles most non-zero buffers.

Signed-off-by: Alexander Monakov 
Signed-off-by: Mikhail Romanov 
Message-Id: <20240206204809.9859-3-amona...@ispras.ru>
[rth: Use __builtin_constant_p; move the indirect call out of line.]
Signed-off-by: Richard Henderson 
---
 include/qemu/cutils.h | 32 -
 util/bufferiszero.c   | 84 +--
 2 files changed, 63 insertions(+), 53 deletions(-)

diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h
index 92c927a6a3..741dade7cf 100644
--- a/include/qemu/cutils.h
+++ b/include/qemu/cutils.h
@@ -187,9 +187,39 @@ char *freq_to_str(uint64_t freq_hz);
 /* used to print char* safely */
 #define STR_OR_NULL(str) ((str) ? (str) : "null")
 
-bool buffer_is_zero(const void *buf, size_t len);
+/*
+ * Check if a buffer is all zeroes.
+ */
+
+bool buffer_is_zero_ool(const void *vbuf, size_t len);
+bool buffer_is_zero_ge256(const void *vbuf, size_t len);
 bool test_buffer_is_zero_next_accel(void);
 
+static inline bool buffer_is_zero_sample3(const char *buf, size_t len)
+{
+/*
+ * For any reasonably sized buffer, these three samples come from
+ * three different cachelines.  In qemu-img usage, we find that
+ * each byte eliminates more than half of all buffer testing.
+ * It is therefore critical to performance that the byte tests
+ * short-circuit, so that we do not pull in additional cache lines.
+ * Do not "optimize" this to !(a | b | c).
+ */
+return !buf[0] && !buf[len - 1] && !buf[len / 2];
+}
+
+#ifdef __OPTIMIZE__
+static inline bool buffer_is_zero(const void *buf, size_t len)
+{
+return (__builtin_constant_p(len) && len >= 256
+? buffer_is_zero_sample3(buf, len) &&
+  buffer_is_zero_ge256(buf, len)
+: buffer_is_zero_ool(buf, len));
+}
+#else
+#define buffer_is_zero  buffer_is_zero_ool
+#endif
+
 /*
  * Implementation of ULEB128 (http://en.wikipedia.org/wiki/LEB128)
  * Input is limited to 14-bit numbers
diff --git a/util/bufferiszero.c b/util/bufferiszero.c
index 641d5f9b9e..972f394cbd 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -26,8 +26,9 @@
 #include "qemu/bswap.h"
 #include "host/cpuinfo.h"
 
-static bool
-buffer_zero_int(const void *buf, size_t len)
+static bool (*buffer_is_zero_accel)(const void *, size_t);
+
+static bool buffer_is_zero_integer(const void *buf, size_t len)
 {
 if (unlikely(len < 8)) {
 /* For a very small buffer, simply accumulate all the bytes.  */
@@ -128,60 +129,38 @@ buffer_zero_avx2(const void *buf, size_t len)
 }
 #endif /* CONFIG_AVX2_OPT */
 
-/*
- * Make sure that these variables are appropriately initialized when
- * SSE2 is enabled on the compiler command-line, but the compiler is
- * too old to support CONFIG_AVX2_OPT.
- */
-#if defined(CONFIG_AVX2_OPT)
-# define INIT_USED 0
-# define INIT_LENGTH   0
-# define INIT_ACCELbuffer_zero_int
-#else
-# ifndef __SSE2__
-#  error "ISA selection confusion"
-# endif
-# define INIT_USED CPUINFO_SSE2
-# define INIT_LENGTH   64
-# define INIT_ACCELbuffer_zero_sse2
-#endif
-
-static unsigned used_accel = INIT_USED;
-static unsigned length_to_accel = INIT_LENGTH;
-static bool (*buffer_accel)(const void *, size_t) = INIT_ACCEL;
-
 static unsigned __attribute__((noinline))
 select_accel_cpuinfo(unsigned info)
 {
 /* Array is sorted in order of algorithm preference. */
 static const struct {
 unsigned bit;
-unsigned len;
 bool (*fn)(const void *, size_t);
 } all[] = {
 #ifdef CONFIG_AVX2_OPT
-{ CPUINFO_AVX2,128, buffer_zero_avx2 },
+{ CPUINFO_AVX2,buffer_zero_avx2 },
 #endif
-{ CPUINFO_SSE2, 64, buffer_zero_sse2 },
-{ CPUINFO_ALWAYS,0, buffer_zero_int },
+{ CPUINFO_SSE2,buffer_zero_sse2 },
+{ CPUINFO_ALWAYS,  buffer_is_zero_integer },
 };
 
 for (unsigned i = 0; i < ARRAY_SIZE(all); ++i) {
 if (info & all[i].bit) {
-length_to_accel = all[i].len;
-buffer_accel = all[i].fn;
+buffer_is_zero_accel = all[i].fn;
 return all[i].bit;
 }
 }
 return 0;
 }
 
-#if defined(CONFIG_AVX2_OPT)
+static unsigned used_accel;
+
 static void __attribute__((constructor)) init_accel(void)
 {
 used_accel = select_accel_cpuinfo(cpuinfo_init());
 }
-#endif /* CONFIG_AVX2_OPT */
+
+#define INIT_ACCEL NULL
 
 bool test_buffer_is_zero_next_accel(void)
 {
@@ -194,36 +173,37 @@ bool test_buffer_is_zero_next_accel(void)
 used_accel |= used;
 return used;
 }
-
-static bool select_accel_fn(const void *buf, size_t len)
-{
-if (likely(len >= length_to_accel)) {
-return buffer_accel(buf, len);
-}
-return buffer_zero_int(buf, len);
-}
-
 #else
-#define select_accel_fn  buffer_zero_int
 bool 

[PATCH v5 09/10] util/bufferiszero: Add simd acceleration for aarch64

2024-02-16 Thread Richard Henderson
Because non-embedded aarch64 is expected to have AdvSIMD enabled, merely
double-check with the compiler flags for __ARM_NEON and don't bother with
a runtime check.  Otherwise, model the loop after the x86 SSE2 function,
and use VADDV to reduce the four vector comparisons.

Signed-off-by: Richard Henderson 
---
 util/bufferiszero.c | 77 +
 1 file changed, 77 insertions(+)

diff --git a/util/bufferiszero.c b/util/bufferiszero.c
index 9b338f7be5..77db305bb0 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -214,7 +214,84 @@ bool test_buffer_is_zero_next_accel(void)
 }
 return false;
 }
+
+#elif defined(__aarch64__) && defined(__ARM_NEON)
+#include 
+
+#define REASSOC_BARRIER(vec0, vec1) asm("" : "+w"(vec0), "+w"(vec1))
+
+static bool buffer_is_zero_simd(const void *buf, size_t len)
+{
+uint32x4_t t0, t1, t2, t3;
+
+/* Align head/tail to 16-byte boundaries.  */
+const uint32x4_t *p = QEMU_ALIGN_PTR_DOWN(buf + 16, 16);
+const uint32x4_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 16);
+
+/* Unaligned loads at head/tail.  */
+t0 = vld1q_u32(buf) | vld1q_u32(buf + len - 16);
+
+/* Collect a partial block at tail end.  */
+t1 = e[-7] | e[-6];
+t2 = e[-5] | e[-4];
+t3 = e[-3] | e[-2];
+t0 |= e[-1];
+REASSOC_BARRIER(t0, t1);
+REASSOC_BARRIER(t2, t3);
+t0 |= t1;
+t2 |= t3;
+REASSOC_BARRIER(t0, t2);
+t0 |= t2;
+
+/*
+ * Loop over complete 128-byte blocks.
+ * With the head and tail removed, e - p >= 14, so the loop
+ * must iterate at least once.
+ */
+do {
+/*
+ * Reduce via UMAXV.  Whatever the actual result,
+ * it will only be zero if all input bytes are zero.
+ */
+if (unlikely(vmaxvq_u32(t0) != 0)) {
+return false;
+}
+
+t0 = p[0] | p[1];
+t1 = p[2] | p[3];
+t2 = p[4] | p[5];
+t3 = p[6] | p[7];
+REASSOC_BARRIER(t0, t1);
+REASSOC_BARRIER(t2, t3);
+t0 |= t1;
+t2 |= t3;
+REASSOC_BARRIER(t0, t2);
+t0 |= t2;
+p += 8;
+} while (p < e - 7);
+
+return vmaxvq_u32(t0) == 0;
+}
+
+static biz_accel_fn const accel_table[] = {
+buffer_is_zero_int_ge256,
+buffer_is_zero_simd,
+};
+
+static unsigned accel_index = 1;
+#define INIT_ACCEL buffer_is_zero_simd
+
+bool test_buffer_is_zero_next_accel(void)
+{
+if (accel_index != 0) {
+buffer_is_zero_accel = accel_table[--accel_index];
+return true;
+}
+return false;
+}
+
 #else
+
 bool test_buffer_is_zero_next_accel(void)
 {
 return false;
-- 
2.34.1




[PATCH v5 08/10] util/bufferiszero: Simplify test_buffer_is_zero_next_accel

2024-02-16 Thread Richard Henderson
Because the three alternatives are monotonic, we don't
need to keep a couple of bitmasks, just identify the
strongest alternative at startup.

Signed-off-by: Richard Henderson 
---
 util/bufferiszero.c | 56 ++---
 1 file changed, 22 insertions(+), 34 deletions(-)

diff --git a/util/bufferiszero.c b/util/bufferiszero.c
index 61ea59d2e0..9b338f7be5 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -180,51 +180,39 @@ buffer_zero_avx2(const void *buf, size_t len)
 }
 #endif /* CONFIG_AVX2_OPT */
 
-
-
-static unsigned __attribute__((noinline))
-select_accel_cpuinfo(unsigned info)
-{
-/* Array is sorted in order of algorithm preference. */
-static const struct {
-unsigned bit;
-biz_accel_fn fn;
-} all[] = {
+static biz_accel_fn const accel_table[] = {
+buffer_is_zero_int_ge256,
+buffer_zero_sse2,
 #ifdef CONFIG_AVX2_OPT
-{ CPUINFO_AVX2,buffer_zero_avx2 },
+buffer_zero_avx2,
 #endif
-{ CPUINFO_SSE2,buffer_zero_sse2 },
-{ CPUINFO_ALWAYS,  buffer_is_zero_int_ge256 },
-};
-
-for (unsigned i = 0; i < ARRAY_SIZE(all); ++i) {
-if (info & all[i].bit) {
-buffer_is_zero_accel = all[i].fn;
-return all[i].bit;
-}
-}
-return 0;
-}
-
-static unsigned used_accel;
+};
+static unsigned accel_index;
 
 static void __attribute__((constructor)) init_accel(void)
 {
-used_accel = select_accel_cpuinfo(cpuinfo_init());
+unsigned info = cpuinfo_init();
+unsigned index = (info & CPUINFO_SSE2 ? 1 : 0);
+
+#ifdef CONFIG_AVX2_OPT
+if (info & CPUINFO_AVX2) {
+index = 2;
+}
+#endif
+
+accel_index = index;
+buffer_is_zero_accel = accel_table[index];
 }
 
 #define INIT_ACCEL NULL
 
 bool test_buffer_is_zero_next_accel(void)
 {
-/*
- * Accumulate the accelerators that we've already tested, and
- * remove them from the set to test this round.  We'll get back
- * a zero from select_accel_cpuinfo when there are no more.
- */
-unsigned used = select_accel_cpuinfo(cpuinfo & ~used_accel);
-used_accel |= used;
-return used;
+if (accel_index != 0) {
+buffer_is_zero_accel = accel_table[--accel_index];
+return true;
+}
+return false;
 }
 #else
 bool test_buffer_is_zero_next_accel(void)
-- 
2.34.1




[PATCH v5 07/10] util/bufferiszero: Introduce biz_accel_fn typedef

2024-02-16 Thread Richard Henderson
Signed-off-by: Richard Henderson 
---
 util/bufferiszero.c | 9 ++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/util/bufferiszero.c b/util/bufferiszero.c
index a904b747c7..61ea59d2e0 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -26,7 +26,8 @@
 #include "qemu/bswap.h"
 #include "host/cpuinfo.h"
 
-static bool (*buffer_is_zero_accel)(const void *, size_t);
+typedef bool (*biz_accel_fn)(const void *, size_t);
+static biz_accel_fn buffer_is_zero_accel;
 
 static bool buffer_is_zero_int_lt256(const void *buf, size_t len)
 {
@@ -179,13 +180,15 @@ buffer_zero_avx2(const void *buf, size_t len)
 }
 #endif /* CONFIG_AVX2_OPT */
 
+
+
 static unsigned __attribute__((noinline))
 select_accel_cpuinfo(unsigned info)
 {
 /* Array is sorted in order of algorithm preference. */
 static const struct {
 unsigned bit;
-bool (*fn)(const void *, size_t);
+biz_accel_fn fn;
 } all[] = {
 #ifdef CONFIG_AVX2_OPT
 { CPUINFO_AVX2,buffer_zero_avx2 },
@@ -232,7 +235,7 @@ bool test_buffer_is_zero_next_accel(void)
 #define INIT_ACCEL buffer_is_zero_int_ge256
 #endif
 
-static bool (*buffer_is_zero_accel)(const void *, size_t) = INIT_ACCEL;
+static biz_accel_fn buffer_is_zero_accel = INIT_ACCEL;
 
 bool buffer_is_zero_ool(const void *buf, size_t len)
 {
-- 
2.34.1




[PATCH v5 04/10] util/bufferiszero: Remove useless prefetches

2024-02-16 Thread Richard Henderson
From: Alexander Monakov 

Use of prefetching in bufferiszero.c is quite questionable:

- prefetches are issued just a few CPU cycles before the corresponding
  line would be hit by demand loads;

- they are done for simple access patterns, i.e. where hardware
  prefetchers can perform better;

- they compete for load ports in loops that should be limited by load
  port throughput rather than ALU throughput.

Signed-off-by: Alexander Monakov 
Signed-off-by: Mikhail Romanov 
Reviewed-by: Richard Henderson 
Message-Id: <20240206204809.9859-5-amona...@ispras.ru>
---
 util/bufferiszero.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/util/bufferiszero.c b/util/bufferiszero.c
index 972f394cbd..00118d649e 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -50,7 +50,6 @@ static bool buffer_is_zero_integer(const void *buf, size_t 
len)
 const uint64_t *e = (uint64_t *)(((uintptr_t)buf + len) & -8);
 
 for (; p + 8 <= e; p += 8) {
-__builtin_prefetch(p + 8);
 if (t) {
 return false;
 }
@@ -80,7 +79,6 @@ buffer_zero_sse2(const void *buf, size_t len)
 
 /* Loop over 16-byte aligned blocks of 64.  */
 while (likely(p <= e)) {
-__builtin_prefetch(p);
 t = _mm_cmpeq_epi8(t, zero);
 if (unlikely(_mm_movemask_epi8(t) != 0x)) {
 return false;
@@ -111,7 +109,6 @@ buffer_zero_avx2(const void *buf, size_t len)
 
 /* Loop over 32-byte aligned blocks of 128.  */
 while (p <= e) {
-__builtin_prefetch(p);
 if (unlikely(!_mm256_testz_si256(t, t))) {
 return false;
 }
-- 
2.34.1




[PATCH v5 02/10] util/bufferiszero: Remove AVX512 variant

2024-02-16 Thread Richard Henderson
From: Alexander Monakov 

Thanks to early checks in the inline buffer_is_zero wrapper, the SIMD
routines are invoked much more rarely in normal use when most buffers
are non-zero. This makes use of AVX512 unprofitable, as it incurs extra
frequency and voltage transition periods during which the CPU operates
at reduced performance, as described in
https://travisdowns.github.io/blog/2020/01/17/avxfreq1.html

Signed-off-by: Mikhail Romanov 
Signed-off-by: Alexander Monakov 
Reviewed-by: Richard Henderson 
Message-Id: <20240206204809.9859-4-amona...@ispras.ru>
Signed-off-by: Richard Henderson 
---
 util/bufferiszero.c | 38 +++---
 1 file changed, 3 insertions(+), 35 deletions(-)

diff --git a/util/bufferiszero.c b/util/bufferiszero.c
index f5a3634f9a..641d5f9b9e 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -64,7 +64,7 @@ buffer_zero_int(const void *buf, size_t len)
 }
 }
 
-#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT) || 
defined(__SSE2__)
+#if defined(CONFIG_AVX2_OPT) || defined(__SSE2__)
 #include 
 
 /* Note that each of these vectorized functions require len >= 64.  */
@@ -128,41 +128,12 @@ buffer_zero_avx2(const void *buf, size_t len)
 }
 #endif /* CONFIG_AVX2_OPT */
 
-#ifdef CONFIG_AVX512F_OPT
-static bool __attribute__((target("avx512f")))
-buffer_zero_avx512(const void *buf, size_t len)
-{
-/* Begin with an unaligned head of 64 bytes.  */
-__m512i t = _mm512_loadu_si512(buf);
-__m512i *p = (__m512i *)(((uintptr_t)buf + 5 * 64) & -64);
-__m512i *e = (__m512i *)(((uintptr_t)buf + len) & -64);
-
-/* Loop over 64-byte aligned blocks of 256.  */
-while (p <= e) {
-__builtin_prefetch(p);
-if (unlikely(_mm512_test_epi64_mask(t, t))) {
-return false;
-}
-t = p[-4] | p[-3] | p[-2] | p[-1];
-p += 4;
-}
-
-t |= _mm512_loadu_si512(buf + len - 4 * 64);
-t |= _mm512_loadu_si512(buf + len - 3 * 64);
-t |= _mm512_loadu_si512(buf + len - 2 * 64);
-t |= _mm512_loadu_si512(buf + len - 1 * 64);
-
-return !_mm512_test_epi64_mask(t, t);
-
-}
-#endif /* CONFIG_AVX512F_OPT */
-
 /*
  * Make sure that these variables are appropriately initialized when
  * SSE2 is enabled on the compiler command-line, but the compiler is
  * too old to support CONFIG_AVX2_OPT.
  */
-#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT)
+#if defined(CONFIG_AVX2_OPT)
 # define INIT_USED 0
 # define INIT_LENGTH   0
 # define INIT_ACCELbuffer_zero_int
@@ -188,9 +159,6 @@ select_accel_cpuinfo(unsigned info)
 unsigned len;
 bool (*fn)(const void *, size_t);
 } all[] = {
-#ifdef CONFIG_AVX512F_OPT
-{ CPUINFO_AVX512F, 256, buffer_zero_avx512 },
-#endif
 #ifdef CONFIG_AVX2_OPT
 { CPUINFO_AVX2,128, buffer_zero_avx2 },
 #endif
@@ -208,7 +176,7 @@ select_accel_cpuinfo(unsigned info)
 return 0;
 }
 
-#if defined(CONFIG_AVX512F_OPT) || defined(CONFIG_AVX2_OPT)
+#if defined(CONFIG_AVX2_OPT)
 static void __attribute__((constructor)) init_accel(void)
 {
 used_accel = select_accel_cpuinfo(cpuinfo_init());
-- 
2.34.1




[PATCH v5 01/10] util/bufferiszero: Remove SSE4.1 variant

2024-02-16 Thread Richard Henderson
From: Alexander Monakov 

The SSE4.1 variant is virtually identical to the SSE2 variant, except
for using 'PTEST+JNZ' in place of 'PCMPEQB+PMOVMSKB+CMP+JNE' for testing
if an SSE register is all zeroes. The PTEST instruction decodes to two
uops, so it can be handled only by the complex decoder, and since
CMP+JNE are macro-fused, both sequences decode to three uops. The uops
comprising the PTEST instruction dispatch to p0 and p5 on Intel CPUs, so
PCMPEQB+PMOVMSKB is comparatively more flexible from dispatch
standpoint.

Hence, the use of PTEST brings no benefit from throughput standpoint.
Its latency is not important, since it feeds only a conditional jump,
which terminates the dependency chain.

I never observed PTEST variants to be faster on real hardware.

Signed-off-by: Alexander Monakov 
Signed-off-by: Mikhail Romanov 
Reviewed-by: Richard Henderson 
Message-Id: <20240206204809.9859-2-amona...@ispras.ru>
---
 util/bufferiszero.c | 29 -
 1 file changed, 29 deletions(-)

diff --git a/util/bufferiszero.c b/util/bufferiszero.c
index 3e6a5dfd63..f5a3634f9a 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -100,34 +100,6 @@ buffer_zero_sse2(const void *buf, size_t len)
 }
 
 #ifdef CONFIG_AVX2_OPT
-static bool __attribute__((target("sse4")))
-buffer_zero_sse4(const void *buf, size_t len)
-{
-__m128i t = _mm_loadu_si128(buf);
-__m128i *p = (__m128i *)(((uintptr_t)buf + 5 * 16) & -16);
-__m128i *e = (__m128i *)(((uintptr_t)buf + len) & -16);
-
-/* Loop over 16-byte aligned blocks of 64.  */
-while (likely(p <= e)) {
-__builtin_prefetch(p);
-if (unlikely(!_mm_testz_si128(t, t))) {
-return false;
-}
-t = p[-4] | p[-3] | p[-2] | p[-1];
-p += 4;
-}
-
-/* Finish the aligned tail.  */
-t |= e[-3];
-t |= e[-2];
-t |= e[-1];
-
-/* Finish the unaligned tail.  */
-t |= _mm_loadu_si128(buf + len - 16);
-
-return _mm_testz_si128(t, t);
-}
-
 static bool __attribute__((target("avx2")))
 buffer_zero_avx2(const void *buf, size_t len)
 {
@@ -221,7 +193,6 @@ select_accel_cpuinfo(unsigned info)
 #endif
 #ifdef CONFIG_AVX2_OPT
 { CPUINFO_AVX2,128, buffer_zero_avx2 },
-{ CPUINFO_SSE4, 64, buffer_zero_sse4 },
 #endif
 { CPUINFO_SSE2, 64, buffer_zero_sse2 },
 { CPUINFO_ALWAYS,0, buffer_zero_int },
-- 
2.34.1




[PATCH v5 06/10] util/bufferiszero: Improve scalar variant

2024-02-16 Thread Richard Henderson
Split less-than and greater-than 256 cases.
Use unaligned accesses for head and tail.
Avoid using out-of-bounds pointers in loop boundary conditions.

Signed-off-by: Richard Henderson 
---
 util/bufferiszero.c | 86 +++--
 1 file changed, 52 insertions(+), 34 deletions(-)

diff --git a/util/bufferiszero.c b/util/bufferiszero.c
index 02df82b4ff..a904b747c7 100644
--- a/util/bufferiszero.c
+++ b/util/bufferiszero.c
@@ -28,40 +28,58 @@
 
 static bool (*buffer_is_zero_accel)(const void *, size_t);
 
-static bool buffer_is_zero_integer(const void *buf, size_t len)
+static bool buffer_is_zero_int_lt256(const void *buf, size_t len)
 {
-if (unlikely(len < 8)) {
-/* For a very small buffer, simply accumulate all the bytes.  */
-const unsigned char *p = buf;
-const unsigned char *e = buf + len;
-unsigned char t = 0;
+uint64_t t;
+const uint64_t *p, *e;
 
-do {
-t |= *p++;
-} while (p < e);
-
-return t == 0;
-} else {
-/* Otherwise, use the unaligned memory access functions to
-   handle the beginning and end of the buffer, with a couple
-   of loops handling the middle aligned section.  */
-uint64_t t = ldq_he_p(buf);
-const uint64_t *p = (uint64_t *)(((uintptr_t)buf + 8) & -8);
-const uint64_t *e = (uint64_t *)(((uintptr_t)buf + len) & -8);
-
-for (; p + 8 <= e; p += 8) {
-if (t) {
-return false;
-}
-t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7];
-}
-while (p < e) {
-t |= *p++;
-}
-t |= ldq_he_p(buf + len - 8);
-
-return t == 0;
+/*
+ * Use unaligned memory access functions to handle
+ * the beginning and end of the buffer, with a couple
+ * of loops handling the middle aligned section.
+ */
+if (unlikely(len <= 8)) {
+return (ldl_he_p(buf) | ldl_he_p(buf + len - 4)) == 0;
 }
+
+t = ldq_he_p(buf) | ldq_he_p(buf + len - 8);
+p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8);
+e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8);
+
+while (p < e) {
+t |= *p++;
+}
+return t == 0;
+}
+
+static bool buffer_is_zero_int_ge256(const void *buf, size_t len)
+{
+/*
+ * Use unaligned memory access functions to handle
+ * the beginning and end of the buffer, with a couple
+ * of loops handling the middle aligned section.
+ */
+uint64_t t = ldq_he_p(buf) | ldq_he_p(buf + len - 8);
+const uint64_t *p = QEMU_ALIGN_PTR_DOWN(buf + 8, 8);
+const uint64_t *e = QEMU_ALIGN_PTR_DOWN(buf + len - 1, 8);
+
+/* Collect a partial block at the tail end. */
+t |= e[-7] | e[-6] | e[-5] | e[-4] | e[-3] | e[-2] | e[-1];
+
+/*
+ * Loop over 64 byte blocks.
+ * With the head and tail removed, e - p >= 30,
+ * so the loop must iterate at least 3 times.
+ */
+do {
+if (t) {
+return false;
+}
+t = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7];
+p += 8;
+} while (p < e - 7);
+
+return t == 0;
 }
 
 #if defined(CONFIG_AVX2_OPT) || defined(__SSE2__)
@@ -173,7 +191,7 @@ select_accel_cpuinfo(unsigned info)
 { CPUINFO_AVX2,buffer_zero_avx2 },
 #endif
 { CPUINFO_SSE2,buffer_zero_sse2 },
-{ CPUINFO_ALWAYS,  buffer_is_zero_integer },
+{ CPUINFO_ALWAYS,  buffer_is_zero_int_ge256 },
 };
 
 for (unsigned i = 0; i < ARRAY_SIZE(all); ++i) {
@@ -211,7 +229,7 @@ bool test_buffer_is_zero_next_accel(void)
 return false;
 }
 
-#define INIT_ACCEL buffer_is_zero_integer
+#define INIT_ACCEL buffer_is_zero_int_ge256
 #endif
 
 static bool (*buffer_is_zero_accel)(const void *, size_t) = INIT_ACCEL;
@@ -232,7 +250,7 @@ bool buffer_is_zero_ool(const void *buf, size_t len)
 if (likely(len >= 256)) {
 return buffer_is_zero_accel(buf, len);
 }
-return buffer_is_zero_integer(buf, len);
+return buffer_is_zero_int_lt256(buf, len);
 }
 
 bool buffer_is_zero_ge256(const void *buf, size_t len)
-- 
2.34.1




[PATCH RFC 4/8] target/riscv: Support generic CSR indirect access

2024-02-16 Thread Atish Patra
From: Kaiwen Xue 

This adds the indirect access registers required by sscsrind/smcsrind
and the operations on them. Note that xiselect and xireg are used for
both AIA and sxcsrind, and the behavior of accessing them depends on
whether each extension is enabled and the value stored in xiselect.

Co-developed-by: Atish Patra 
Signed-off-by: Atish Patra 
Signed-off-by: Kaiwen Xue 
---
 target/riscv/cpu_bits.h |  28 +++-
 target/riscv/csr.c  | 146 +++-
 2 files changed, 169 insertions(+), 5 deletions(-)

diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 0ee91e502e8f..3a66f83009b5 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -176,6 +176,13 @@
 #define CSR_MISELECT0x350
 #define CSR_MIREG   0x351
 
+/* Machine Indirect Register Alias */
+#define CSR_MIREG2  0x352
+#define CSR_MIREG3  0x353
+#define CSR_MIREG4  0x355
+#define CSR_MIREG5  0x356
+#define CSR_MIREG6  0x357
+
 /* Machine-Level Interrupts (AIA) */
 #define CSR_MTOPEI  0x35c
 #define CSR_MTOPI   0xfb0
@@ -225,6 +232,13 @@
 #define CSR_SISELECT0x150
 #define CSR_SIREG   0x151
 
+/* Supervisor Indirect Register Alias */
+#define CSR_SIREG2  0x152
+#define CSR_SIREG3  0x153
+#define CSR_SIREG4  0x155
+#define CSR_SIREG5  0x156
+#define CSR_SIREG6  0x157
+
 /* Supervisor-Level Interrupts (AIA) */
 #define CSR_STOPEI  0x15c
 #define CSR_STOPI   0xdb0
@@ -291,6 +305,13 @@
 #define CSR_VSISELECT   0x250
 #define CSR_VSIREG  0x251
 
+/* Virtual Supervisor Indirect Alias */
+#define CSR_VSIREG2 0x252
+#define CSR_VSIREG3 0x253
+#define CSR_VSIREG4 0x255
+#define CSR_VSIREG5 0x256
+#define CSR_VSIREG6 0x257
+
 /* VS-Level Interrupts (H-extension with AIA) */
 #define CSR_VSTOPEI 0x25c
 #define CSR_VSTOPI  0xeb0
@@ -847,10 +868,13 @@ typedef enum RISCVException {
 #define ISELECT_IMSIC_EIE630xff
 #define ISELECT_IMSIC_FIRSTISELECT_IMSIC_EIDELIVERY
 #define ISELECT_IMSIC_LAST ISELECT_IMSIC_EIE63
-#define ISELECT_MASK   0x1ff
+#define ISELECT_MASK_AIA   0x1ff
+
+/* MISELECT, SISELECT, and VSISELECT bits (AIA) */
+#define ISELECT_MASK_SXCSRIND  0xfff
 
 /* Dummy [M|S|VS]ISELECT value for emulating [M|S|VS]TOPEI CSRs */
-#define ISELECT_IMSIC_TOPEI(ISELECT_MASK + 1)
+#define ISELECT_IMSIC_TOPEI(ISELECT_MASK_AIA + 1)
 
 /* IMSIC bits (AIA) */
 #define IMSIC_TOPEI_IID_SHIFT  16
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 89a1325a02a5..a1c10f1d010a 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -287,6 +287,17 @@ static int aia_any32(CPURISCVState *env, int csrno)
 return any32(env, csrno);
 }
 
+static RISCVException sxcsrind_any(CPURISCVState *env, int csrno)
+{
+RISCVCPU *cpu = env_archcpu(env);
+
+if (!cpu->cfg.ext_smcsrind) {
+return RISCV_EXCP_ILLEGAL_INST;
+}
+
+return RISCV_EXCP_NONE;
+}
+
 static int sxcsrind_or_aia_any(CPURISCVState *env, int csrno)
 {
 if (!riscv_cpu_cfg(env)->ext_smaia && !riscv_cpu_cfg(env)->ext_smcsrind) {
@@ -355,6 +366,17 @@ static int aia_smode32(CPURISCVState *env, int csrno)
 return smode32(env, csrno);
 }
 
+static RISCVException sxcsrind_smode(CPURISCVState *env, int csrno)
+{
+RISCVCPU *cpu = env_archcpu(env);
+
+if (!cpu->cfg.ext_sscsrind) {
+return RISCV_EXCP_ILLEGAL_INST;
+}
+
+return smode(env, csrno);
+}
+
 static int sxcsrind_or_aia_smode(CPURISCVState *env, int csrno)
 {
 if (!riscv_cpu_cfg(env)->ext_ssaia && !riscv_cpu_cfg(env)->ext_sscsrind) {
@@ -383,6 +405,17 @@ static RISCVException hmode32(CPURISCVState *env, int 
csrno)
 
 }
 
+static RISCVException sxcsrind_hmode(CPURISCVState *env, int csrno)
+{
+RISCVCPU *cpu = env_archcpu(env);
+
+if (!cpu->cfg.ext_sscsrind) {
+return RISCV_EXCP_ILLEGAL_INST;
+}
+
+return hmode(env, csrno);
+}
+
 static int sxcsrind_or_aia_hmode(CPURISCVState *env, int csrno)
 {
 if (!riscv_cpu_cfg(env)->ext_ssaia && !riscv_cpu_cfg(env)->ext_sscsrind) {
@@ -1926,7 +1959,12 @@ static int rmw_xiselect(CPURISCVState *env, int csrno, 
target_ulong *val,
 *val = *iselect;
 }
 
-wr_mask &= ISELECT_MASK;
+if (riscv_cpu_cfg(env)->ext_smcsrind || riscv_cpu_cfg(env)->ext_sscsrind) {
+wr_mask &= ISELECT_MASK_SXCSRIND;
+} else {
+wr_mask &= ISELECT_MASK_AIA;
+}
+
 if (wr_mask) {
 *iselect = (*iselect & ~wr_mask) | (new_val & wr_mask);
 }
@@ -2065,6 +2103,59 @@ done:
 return RISCV_EXCP_NONE;
 }
 
+/*
+ * rmw_xireg_sxcsrind: Perform indirect access to xireg and xireg2-xireg6
+ *
+ * Perform indirect access to xireg and xireg2-xireg6.
+ * This is a generic interface for all xireg CSRs. Apart from 

[PATCH RFC 3/8] target/riscv: Enable S*stateen bits for AIA

2024-02-16 Thread Atish Patra
As per the ratified AIA spec v1.0, three stateen bits control AIA CSR
access.

Bit 60 controls the indirect CSRs
Bit 59 controls the most AIA CSR state
Bit 58 controls the IMSIC state such as stopei and vstopei

Enable the corresponding bits in [m|h]stateen and enable corresponding
checks in the CSR accessor functions.

Signed-off-by: Atish Patra 
---
 target/riscv/csr.c | 89 +-
 1 file changed, 88 insertions(+), 1 deletion(-)

diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 1af0c8890a2b..89a1325a02a5 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -316,19 +316,42 @@ static int smode32(CPURISCVState *env, int csrno)
 
 static int aia_smode(CPURISCVState *env, int csrno)
 {
+int ret;
+
 if (!riscv_cpu_cfg(env)->ext_ssaia) {
 return RISCV_EXCP_ILLEGAL_INST;
 }
 
+if (csrno == CSR_STOPEI) {
+ret = smstateen_acc_ok(env, 0, SMSTATEEN0_IMSIC);
+} else {
+ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA);
+}
+
+if (ret != RISCV_EXCP_NONE) {
+return ret;
+}
+
 return smode(env, csrno);
 }
 
 static int aia_smode32(CPURISCVState *env, int csrno)
 {
+int ret;
+
 if (!riscv_cpu_cfg(env)->ext_ssaia) {
 return RISCV_EXCP_ILLEGAL_INST;
 }
 
+ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA);
+if (ret != RISCV_EXCP_NONE) {
+return ret;
+}
+
+if (ret != RISCV_EXCP_NONE) {
+return ret;
+}
+
 return smode32(env, csrno);
 }
 
@@ -552,15 +575,38 @@ static RISCVException pointer_masking(CPURISCVState *env, 
int csrno)
 
 static int aia_hmode(CPURISCVState *env, int csrno)
 {
+int ret;
+
 if (!riscv_cpu_cfg(env)->ext_ssaia) {
 return RISCV_EXCP_ILLEGAL_INST;
  }
 
- return hmode(env, csrno);
+if (csrno == CSR_VSTOPEI) {
+ret = smstateen_acc_ok(env, 0, SMSTATEEN0_IMSIC);
+} else {
+ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA);
+}
+
+if (ret != RISCV_EXCP_NONE) {
+return ret;
+}
+
+return hmode(env, csrno);
 }
 
 static int aia_hmode32(CPURISCVState *env, int csrno)
 {
+int ret;
+
+if (!riscv_cpu_cfg(env)->ext_ssaia) {
+return RISCV_EXCP_ILLEGAL_INST;
+ }
+
+ret = smstateen_acc_ok(env, 0, SMSTATEEN0_AIA);
+if (ret != RISCV_EXCP_NONE) {
+return ret;
+}
+
 if (!riscv_cpu_cfg(env)->ext_ssaia) {
 return RISCV_EXCP_ILLEGAL_INST;
 }
@@ -1851,6 +1897,12 @@ static int rmw_xiselect(CPURISCVState *env, int csrno, 
target_ulong *val,
 target_ulong new_val, target_ulong wr_mask)
 {
 target_ulong *iselect;
+int ret;
+
+ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT);
+if (ret != RISCV_EXCP_NONE) {
+return ret;
+}
 
 /* Translate CSR number for VS-mode */
 csrno = sxcsrind_xlate_vs_csrno(env, csrno);
@@ -2020,6 +2072,11 @@ static int rmw_xireg(CPURISCVState *env, int csrno, 
target_ulong *val,
 int ret = -EINVAL;
 target_ulong isel;
 
+ret = smstateen_acc_ok(env, 0, SMSTATEEN0_SVSLCT);
+if (ret != RISCV_EXCP_NONE) {
+return ret;
+}
+
 /* Translate CSR number for VS-mode */
 csrno = sxcsrind_xlate_vs_csrno(env, csrno);
 
@@ -2419,6 +2476,23 @@ static RISCVException write_mstateen0(CPURISCVState 
*env, int csrno,
 wr_mask |= SMSTATEEN0_FCSR;
 }
 
+/*
+ * TODO: Do we need to check ssaia as well ? Can we enable ssaia without
+ * smaia ?
+ */
+if (riscv_cpu_cfg(env)->ext_smaia) {
+wr_mask |= SMSTATEEN0_SVSLCT;
+}
+
+/*
+ * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
+ * implemented. However, that information is with MachineState and we can't
+ * figure that out in csr.c. Just enable if Smaia is available.
+ */
+if (riscv_cpu_cfg(env)->ext_smaia) {
+wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
+}
+
 return write_mstateen(env, csrno, wr_mask, new_val);
 }
 
@@ -2495,6 +2569,19 @@ static RISCVException write_hstateen0(CPURISCVState 
*env, int csrno,
 wr_mask |= SMSTATEEN0_FCSR;
 }
 
+if (riscv_cpu_cfg(env)->ext_ssaia) {
+wr_mask |= SMSTATEEN0_SVSLCT;
+}
+
+/*
+ * As per the AIA specification, SMSTATEEN0_IMSIC is valid only if IMSIC is
+ * implemented. However, that information is with MachineState and we can't
+ * figure that out in csr.c. Just enable if Ssaia is available.
+ */
+if (riscv_cpu_cfg(env)->ext_ssaia) {
+wr_mask |= (SMSTATEEN0_AIA | SMSTATEEN0_IMSIC);
+}
+
 return write_hstateen(env, csrno, wr_mask, new_val);
 }
 
-- 
2.34.1




[PATCH RFC 5/8] target/riscv: Add smcdeleg/ssccfg properties

2024-02-16 Thread Atish Patra
From: Kaiwen Xue 

This adds the properties of smcdeleg/ssccfg. Implementation will be in
future patches.

Signed-off-by: Atish Patra 
Signed-off-by: Kaiwen Xue 
---
 target/riscv/cpu.c | 4 
 target/riscv/cpu_cfg.h | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index ff7c6c7c380e..c5ec203fb8fd 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -151,11 +151,13 @@ const RISCVIsaExtData isa_edata_arr[] = {
 ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx),
 ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin),
 ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia),
+ISA_EXT_DATA_ENTRY(smcdeleg, PRIV_VERSION_1_12_0, ext_smcdeleg),
 ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf),
 ISA_EXT_DATA_ENTRY(smcsrind, PRIV_VERSION_1_12_0, ext_smcsrind),
 ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp),
 ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen),
 ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia),
+ISA_EXT_DATA_ENTRY(ssccfg, PRIV_VERSION_1_12_0, ext_ssccfg),
 ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf),
 ISA_EXT_DATA_ENTRY(sscsrind, PRIV_VERSION_1_12_0, ext_sscsrind),
 ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc),
@@ -1352,6 +1354,8 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = {
 MULTI_EXT_CFG_BOOL("smcntrpmf", ext_smcntrpmf, false),
 MULTI_EXT_CFG_BOOL("smcsrind", ext_smcsrind, false),
 MULTI_EXT_CFG_BOOL("sscsrind", ext_sscsrind, false),
+MULTI_EXT_CFG_BOOL("smcdeleg", ext_smcdeleg, false),
+MULTI_EXT_CFG_BOOL("ssccfg", ext_ssccfg, false),
 MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true),
 MULTI_EXT_CFG_BOOL("zicsr", ext_zicsr, true),
 MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true),
diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
index b9086464752e..a3be34c88ef0 100644
--- a/target/riscv/cpu_cfg.h
+++ b/target/riscv/cpu_cfg.h
@@ -76,6 +76,8 @@ struct RISCVCPUConfig {
 bool ext_smcntrpmf;
 bool ext_smcsrind;
 bool ext_sscsrind;
+bool ext_smcdeleg;
+bool ext_ssccfg;
 bool ext_svadu;
 bool ext_svinval;
 bool ext_svnapot;
-- 
2.34.1




[PATCH RFC 7/8] target/riscv: Add select value range check for counter delegation

2024-02-16 Thread Atish Patra
From: Kaiwen Xue 

This adds checks in ops performed on xireg and xireg2-xireg6 so that the
counter delegation function will receive a valid xiselect value with the
proper extensions enabled.

Co-developed-by: Atish Patra 
Signed-off-by: Kaiwen Xue 
Signed-off-by: Atish Patra 
---
 target/riscv/csr.c | 36 +++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index a1c10f1d010a..d5218a47ffbf 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -1978,6 +1978,11 @@ static bool xiselect_aia_range(target_ulong isel)
(ISELECT_IMSIC_FIRST <= isel && isel <= ISELECT_IMSIC_LAST);
 }
 
+static bool xiselect_cd_range(target_ulong isel)
+{
+return (ISELECT_CD_FIRST <= isel && isel <= ISELECT_CD_LAST);
+}
+
 static int rmw_iprio(target_ulong xlen,
  target_ulong iselect, uint8_t *iprio,
  target_ulong *val, target_ulong new_val,
@@ -2103,6 +2108,17 @@ done:
 return RISCV_EXCP_NONE;
 }
 
+static int rmw_xireg_cd(CPURISCVState *env, int csrno,
+target_ulong isel, target_ulong *val,
+target_ulong new_val, target_ulong wr_mask)
+{
+if (!riscv_cpu_cfg(env)->ext_smcdeleg) {
+return RISCV_EXCP_ILLEGAL_INST;
+}
+/* TODO: Implement the functionality later */
+return RISCV_EXCP_NONE;
+}
+
 /*
  * rmw_xireg_sxcsrind: Perform indirect access to xireg and xireg2-xireg6
  *
@@ -2114,7 +2130,25 @@ static int rmw_xireg_sxcsrind(CPURISCVState *env, int 
csrno,
   target_ulong isel, target_ulong *val,
   target_ulong new_val, target_ulong wr_mask)
 {
-return -EINVAL;
+int ret = -EINVAL;
+bool virt = csrno == CSR_VSIREG ? true : false;
+
+if (xiselect_cd_range(isel)) {
+ret = rmw_xireg_cd(env, csrno, isel, val, new_val, wr_mask);
+} else {
+/*
+ * As per the specification, access to unimplented region is undefined
+ * but recommendation is to raise illegal instruction exception.
+ */
+return RISCV_EXCP_ILLEGAL_INST;
+}
+
+if (ret) {
+return (env->virt_enabled && virt) ?
+   RISCV_EXCP_VIRT_INSTRUCTION_FAULT : RISCV_EXCP_ILLEGAL_INST;
+}
+
+return RISCV_EXCP_NONE;
 }
 
 static int rmw_xiregi(CPURISCVState *env, int csrno, target_ulong *val,
-- 
2.34.1




[PATCH RFC 1/8] target/riscv: Add properties for Indirect CSR Access extension

2024-02-16 Thread Atish Patra
From: Kaiwen Xue 

This adds the properties for sxcsrind. Definitions of new registers and
implementations will come with future patches.

Signed-off-by: Atish Patra 
Signed-off-by: Kaiwen Xue 
---
 target/riscv/cpu.c | 4 
 target/riscv/cpu_cfg.h | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 8af99ed2f6de..ff7c6c7c380e 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -152,10 +152,12 @@ const RISCVIsaExtData isa_edata_arr[] = {
 ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin),
 ISA_EXT_DATA_ENTRY(smaia, PRIV_VERSION_1_12_0, ext_smaia),
 ISA_EXT_DATA_ENTRY(smcntrpmf, PRIV_VERSION_1_12_0, ext_smcntrpmf),
+ISA_EXT_DATA_ENTRY(smcsrind, PRIV_VERSION_1_12_0, ext_smcsrind),
 ISA_EXT_DATA_ENTRY(smepmp, PRIV_VERSION_1_12_0, ext_smepmp),
 ISA_EXT_DATA_ENTRY(smstateen, PRIV_VERSION_1_12_0, ext_smstateen),
 ISA_EXT_DATA_ENTRY(ssaia, PRIV_VERSION_1_12_0, ext_ssaia),
 ISA_EXT_DATA_ENTRY(sscofpmf, PRIV_VERSION_1_12_0, ext_sscofpmf),
+ISA_EXT_DATA_ENTRY(sscsrind, PRIV_VERSION_1_12_0, ext_sscsrind),
 ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc),
 ISA_EXT_DATA_ENTRY(svadu, PRIV_VERSION_1_12_0, ext_svadu),
 ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval),
@@ -1348,6 +1350,8 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = {
 /* Defaults for standard extensions */
 MULTI_EXT_CFG_BOOL("sscofpmf", ext_sscofpmf, false),
 MULTI_EXT_CFG_BOOL("smcntrpmf", ext_smcntrpmf, false),
+MULTI_EXT_CFG_BOOL("smcsrind", ext_smcsrind, false),
+MULTI_EXT_CFG_BOOL("sscsrind", ext_sscsrind, false),
 MULTI_EXT_CFG_BOOL("zifencei", ext_zifencei, true),
 MULTI_EXT_CFG_BOOL("zicsr", ext_zicsr, true),
 MULTI_EXT_CFG_BOOL("zihintntl", ext_zihintntl, true),
diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h
index eabbecb8f962..b9086464752e 100644
--- a/target/riscv/cpu_cfg.h
+++ b/target/riscv/cpu_cfg.h
@@ -74,6 +74,8 @@ struct RISCVCPUConfig {
 bool ext_smstateen;
 bool ext_sstc;
 bool ext_smcntrpmf;
+bool ext_smcsrind;
+bool ext_sscsrind;
 bool ext_svadu;
 bool ext_svinval;
 bool ext_svnapot;
-- 
2.34.1




[PATCH RFC 6/8] target/riscv: Add counter delegation definitions

2024-02-16 Thread Atish Patra
From: Kaiwen Xue 

This adds definitions for counter delegation, including the new
scountinhibit register and the mstateen.CD bit.

Signed-off-by: Atish Patra 
Signed-off-by: Kaiwen Xue 
---
 target/riscv/cpu.h  | 1 +
 target/riscv/cpu_bits.h | 8 +++-
 target/riscv/machine.c  | 1 +
 3 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 85afde48fade..d7dcfdb0e5e0 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -342,6 +342,7 @@ struct CPUArchState {
 target_ulong scounteren;
 target_ulong mcounteren;
 
+target_ulong scountinhibit;
 target_ulong mcountinhibit;
 
 /* PMU cycle & instret privilege mode filtering */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 3a66f83009b5..0bffec3476ab 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -213,6 +213,9 @@
 #define CSR_SSTATEEN2   0x10E
 #define CSR_SSTATEEN3   0x10F
 
+/* Supervisor Counter Delegation */
+#define CSR_SCOUNTINHIBIT   0x120
+
 /* Supervisor Trap Handling */
 #define CSR_SSCRATCH0x140
 #define CSR_SEPC0x141
@@ -779,6 +782,7 @@ typedef enum RISCVException {
 #define MENVCFG_CBIE   (3UL << 4)
 #define MENVCFG_CBCFE  BIT(6)
 #define MENVCFG_CBZE   BIT(7)
+#define MENVCFG_CDE(1ULL << 60)
 #define MENVCFG_ADUE   (1ULL << 61)
 #define MENVCFG_PBMTE  (1ULL << 62)
 #define MENVCFG_STCE   (1ULL << 63)
@@ -870,7 +874,9 @@ typedef enum RISCVException {
 #define ISELECT_IMSIC_LAST ISELECT_IMSIC_EIE63
 #define ISELECT_MASK_AIA   0x1ff
 
-/* MISELECT, SISELECT, and VSISELECT bits (AIA) */
+/* [M|S|VS]SELCT value for Indirect CSR Access Extension */
+#define ISELECT_CD_FIRST   0x40
+#define ISELECT_CD_LAST0x5f
 #define ISELECT_MASK_SXCSRIND  0xfff
 
 /* Dummy [M|S|VS]ISELECT value for emulating [M|S|VS]TOPEI CSRs */
diff --git a/target/riscv/machine.c b/target/riscv/machine.c
index 72fe2374dc2a..d26742f99ed7 100644
--- a/target/riscv/machine.c
+++ b/target/riscv/machine.c
@@ -400,6 +400,7 @@ const VMStateDescription vmstate_riscv_cpu = {
 VMSTATE_UINTTL(env.siselect, RISCVCPU),
 VMSTATE_UINTTL(env.scounteren, RISCVCPU),
 VMSTATE_UINTTL(env.mcounteren, RISCVCPU),
+VMSTATE_UINTTL(env.scountinhibit, RISCVCPU),
 VMSTATE_UINTTL(env.mcountinhibit, RISCVCPU),
 VMSTATE_STRUCT_ARRAY(env.pmu_ctrs, RISCVCPU, RV_MAX_MHPMCOUNTERS, 0,
  vmstate_pmu_ctr_state, PMUCTRState),
-- 
2.34.1




[PATCH RFC 2/8] target/riscv: Decouple AIA processing from xiselect and xireg

2024-02-16 Thread Atish Patra
From: Kaiwen Xue 

Since xiselect and xireg also will be of use in sxcsrind, AIA should
have its own separated interface when those CSRs are accessed.

Signed-off-by: Atish Patra 
Signed-off-by: Kaiwen Xue 
---
 target/riscv/csr.c | 147 +
 1 file changed, 122 insertions(+), 25 deletions(-)

diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 8829dee7bc75..1af0c8890a2b 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -287,6 +287,15 @@ static int aia_any32(CPURISCVState *env, int csrno)
 return any32(env, csrno);
 }
 
+static int sxcsrind_or_aia_any(CPURISCVState *env, int csrno)
+{
+if (!riscv_cpu_cfg(env)->ext_smaia && !riscv_cpu_cfg(env)->ext_smcsrind) {
+return RISCV_EXCP_ILLEGAL_INST;
+}
+
+return any(env, csrno);
+}
+
 static RISCVException smode(CPURISCVState *env, int csrno)
 {
 if (riscv_has_ext(env, RVS)) {
@@ -323,6 +332,15 @@ static int aia_smode32(CPURISCVState *env, int csrno)
 return smode32(env, csrno);
 }
 
+static int sxcsrind_or_aia_smode(CPURISCVState *env, int csrno)
+{
+if (!riscv_cpu_cfg(env)->ext_ssaia && !riscv_cpu_cfg(env)->ext_sscsrind) {
+return RISCV_EXCP_ILLEGAL_INST;
+}
+
+return smode(env, csrno);
+}
+
 static RISCVException hmode(CPURISCVState *env, int csrno)
 {
 if (riscv_has_ext(env, RVH)) {
@@ -342,6 +360,15 @@ static RISCVException hmode32(CPURISCVState *env, int 
csrno)
 
 }
 
+static int sxcsrind_or_aia_hmode(CPURISCVState *env, int csrno)
+{
+if (!riscv_cpu_cfg(env)->ext_ssaia && !riscv_cpu_cfg(env)->ext_sscsrind) {
+return RISCV_EXCP_ILLEGAL_INST;
+}
+
+return hmode(env, csrno);
+}
+
 static RISCVException umode(CPURISCVState *env, int csrno)
 {
 if (riscv_has_ext(env, RVU)) {
@@ -1804,13 +1831,29 @@ static int aia_xlate_vs_csrno(CPURISCVState *env, int 
csrno)
 };
 }
 
+static int sxcsrind_xlate_vs_csrno(CPURISCVState *env, int csrno)
+{
+if (!env->virt_enabled) {
+return csrno;
+}
+
+switch (csrno) {
+case CSR_SISELECT:
+return CSR_VSISELECT;
+case CSR_SIREG:
+return CSR_VSIREG;
+default:
+return csrno;
+};
+}
+
 static int rmw_xiselect(CPURISCVState *env, int csrno, target_ulong *val,
 target_ulong new_val, target_ulong wr_mask)
 {
 target_ulong *iselect;
 
 /* Translate CSR number for VS-mode */
-csrno = aia_xlate_vs_csrno(env, csrno);
+csrno = sxcsrind_xlate_vs_csrno(env, csrno);
 
 /* Find the iselect CSR based on CSR number */
 switch (csrno) {
@@ -1839,6 +1882,12 @@ static int rmw_xiselect(CPURISCVState *env, int csrno, 
target_ulong *val,
 return RISCV_EXCP_NONE;
 }
 
+static bool xiselect_aia_range(target_ulong isel)
+{
+return (ISELECT_IPRIO0 <= isel && isel <= ISELECT_IPRIO15) ||
+   (ISELECT_IMSIC_FIRST <= isel && isel <= ISELECT_IMSIC_LAST);
+}
+
 static int rmw_iprio(target_ulong xlen,
  target_ulong iselect, uint8_t *iprio,
  target_ulong *val, target_ulong new_val,
@@ -1884,44 +1933,44 @@ static int rmw_iprio(target_ulong xlen,
 return 0;
 }
 
-static int rmw_xireg(CPURISCVState *env, int csrno, target_ulong *val,
- target_ulong new_val, target_ulong wr_mask)
+static int rmw_xireg_aia(CPURISCVState *env, int csrno,
+ target_ulong isel, target_ulong *val,
+ target_ulong new_val, target_ulong wr_mask)
 {
-bool virt, isel_reserved;
-uint8_t *iprio;
+bool virt = false, isel_reserved = false;
 int ret = -EINVAL;
-target_ulong priv, isel, vgein;
-
-/* Translate CSR number for VS-mode */
-csrno = aia_xlate_vs_csrno(env, csrno);
+uint8_t *iprio;
+target_ulong priv, vgein;
 
-/* Decode register details from CSR number */
-virt = false;
-isel_reserved = false;
+/* VS-mode CSR number passed in has already been translated */
 switch (csrno) {
 case CSR_MIREG:
+if (!riscv_cpu_cfg(env)->ext_smaia) {
+goto done;
+}
 iprio = env->miprio;
-isel = env->miselect;
 priv = PRV_M;
 break;
 case CSR_SIREG:
-if (env->priv == PRV_S && env->mvien & MIP_SEIP &&
+if (!riscv_cpu_cfg(env)->ext_ssaia ||
+(env->priv == PRV_S && env->mvien & MIP_SEIP &&
 env->siselect >= ISELECT_IMSIC_EIDELIVERY &&
-env->siselect <= ISELECT_IMSIC_EIE63) {
+env->siselect <= ISELECT_IMSIC_EIE63)) {
 goto done;
 }
 iprio = env->siprio;
-isel = env->siselect;
 priv = PRV_S;
 break;
 case CSR_VSIREG:
+if (!riscv_cpu_cfg(env)->ext_ssaia) {
+goto done;
+}
 iprio = env->hviprio;
-isel = env->vsiselect;
 priv = PRV_S;
 virt = true;
 break;
 default:
- goto done;
+goto done;
 };
 
 /* Find the 

[PATCH RFC 8/8] target/riscv: Add counter delegation/configuration support

2024-02-16 Thread Atish Patra
From: Kaiwen Xue 

The Smcdeleg/Ssccfg adds the support for counter delegation via
S*indcsr and Ssccfg.

It also adds a new shadow CSR scountinhibit and menvcfg enable bit (CDE)
to enable this extension and scountovf virtualization.

Signed-off-by: Kaiwen Xue 
Co-developed-by: Atish Patra 
Signed-off-by: Atish Patra 
---
 target/riscv/csr.c | 307 +++--
 1 file changed, 294 insertions(+), 13 deletions(-)

diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index d5218a47ffbf..3542c522ba07 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -366,6 +366,21 @@ static int aia_smode32(CPURISCVState *env, int csrno)
 return smode32(env, csrno);
 }
 
+static RISCVException scountinhibit_pred(CPURISCVState *env, int csrno)
+{
+RISCVCPU *cpu = env_archcpu(env);
+
+if (!cpu->cfg.ext_ssccfg || !cpu->cfg.ext_smcdeleg) {
+return RISCV_EXCP_ILLEGAL_INST;
+}
+
+if (env->virt_enabled) {
+return RISCV_EXCP_VIRT_INSTRUCTION_FAULT;
+}
+
+return smode(env, csrno);
+}
+
 static RISCVException sxcsrind_smode(CPURISCVState *env, int csrno)
 {
 RISCVCPU *cpu = env_archcpu(env);
@@ -1089,9 +1104,9 @@ done:
 return result;
 }
 
-static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val)
+static RISCVException riscv_pmu_write_ctr(CPURISCVState *env, target_ulong val,
+  uint32_t ctr_idx)
 {
-int ctr_idx = csrno - CSR_MCYCLE;
 PMUCTRState *counter = >pmu_ctrs[ctr_idx];
 uint64_t mhpmctr_val = val;
 
@@ -1115,9 +1130,9 @@ static int write_mhpmcounter(CPURISCVState *env, int 
csrno, target_ulong val)
 return RISCV_EXCP_NONE;
 }
 
-static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val)
+static RISCVException riscv_pmu_write_ctrh(CPURISCVState *env, target_ulong 
val,
+  uint32_t ctr_idx)
 {
-int ctr_idx = csrno - CSR_MCYCLEH;
 PMUCTRState *counter = >pmu_ctrs[ctr_idx];
 uint64_t mhpmctr_val = counter->mhpmcounter_val;
 uint64_t mhpmctrh_val = val;
@@ -1138,6 +1153,20 @@ static int write_mhpmcounterh(CPURISCVState *env, int 
csrno, target_ulong val)
 return RISCV_EXCP_NONE;
 }
 
+static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val)
+{
+int ctr_idx = csrno - CSR_MCYCLE;
+
+return riscv_pmu_write_ctr(env, val, ctr_idx);
+}
+
+static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val)
+{
+int ctr_idx = csrno - CSR_MCYCLEH;
+
+return riscv_pmu_write_ctrh(env, val, ctr_idx);
+}
+
 static RISCVException riscv_pmu_read_ctr(CPURISCVState *env, target_ulong *val,
  bool upper_half, uint32_t ctr_idx)
 {
@@ -1207,6 +1236,167 @@ static int read_hpmcounterh(CPURISCVState *env, int 
csrno, target_ulong *val)
 return riscv_pmu_read_ctr(env, val, true, ctr_index);
 }
 
+static int rmw_cd_mhpmcounter(CPURISCVState *env, int ctr_idx,
+  target_ulong *val, target_ulong new_val,
+  target_ulong wr_mask)
+{
+if (wr_mask != 0 && wr_mask != -1) {
+return -EINVAL;
+}
+
+if (!wr_mask && val) {
+riscv_pmu_read_ctr(env, val, false, ctr_idx);
+} else if (wr_mask) {
+riscv_pmu_write_ctr(env, new_val, ctr_idx);
+} else {
+return -EINVAL;
+}
+
+return 0;
+}
+
+static int rmw_cd_mhpmcounterh(CPURISCVState *env, int ctr_idx,
+   target_ulong *val, target_ulong new_val,
+   target_ulong wr_mask)
+{
+if (wr_mask != 0 && wr_mask != -1) {
+return -EINVAL;
+}
+
+if (!wr_mask && val) {
+riscv_pmu_read_ctr(env, val, true, ctr_idx);
+} else if (wr_mask) {
+riscv_pmu_write_ctrh(env, new_val, ctr_idx);
+} else {
+return -EINVAL;
+}
+
+return 0;
+}
+
+static int rmw_cd_mhpmevent(CPURISCVState *env, int evt_index,
+target_ulong *val, target_ulong new_val,
+target_ulong wr_mask)
+{
+uint64_t mhpmevt_val = new_val;
+
+if (wr_mask != 0 && wr_mask != -1) {
+return -EINVAL;
+}
+
+if (!wr_mask && val) {
+*val = env->mhpmevent_val[evt_index];
+if (riscv_cpu_cfg(env)->ext_sscofpmf) {
+*val &= ~MHPMEVENT_BIT_MINH;
+}
+} else if (wr_mask) {
+wr_mask &= ~MHPMEVENT_BIT_MINH;
+mhpmevt_val = (new_val & wr_mask) |
+  (env->mhpmevent_val[evt_index] & ~wr_mask);
+if (riscv_cpu_mxl(env) == MXL_RV32) {
+mhpmevt_val = mhpmevt_val |
+  ((uint64_t)env->mhpmeventh_val[evt_index] << 32);
+}
+env->mhpmevent_val[evt_index] = mhpmevt_val;
+riscv_pmu_update_event_map(env, mhpmevt_val, evt_index);
+} else {
+return -EINVAL;
+}
+
+return 0;
+}
+
+static int 

[PATCH RFC 0/8] Add Counter delegation ISA extension support

2024-02-16 Thread Atish Patra
This series adds the counter delegation extension support. The counter
delegation ISA extension(Smcdeleg/Ssccfg) actually depends on multiple ISA
extensions.

1. S[m|s]csrind : The indirect CSR extension[1] which defines additional
   5 ([M|S|VS]IREG2-[M|S|VS]IREG6) register to address size limitation of
   RISC-V CSR address space.
2. Smstateen: The stateen bit[60] controls the access to the registers
   indirectly via the above indirect registers.
3. Smcdeleg/Ssccfg: The counter delegation extensions[2]

The counter delegation extension allows Supervisor mode to program the
hpmevent and hpmcounters directly without needing the assistance from the
M-mode via SBI calls. This results in a faster perf profiling and very
few traps. This extension also introduces a scountinhibit CSR which allows
to stop/start any counter directly from the S-mode. As the counter
delegation extension potentially can have more than 100 CSRs, the specificaiton
leverages the indirect CSR extension to save the precious CSR address range.

Due to the dependancy of these extensions, the following extensions must be
enabled to use the counter delegation feature in S-mode.

"smstateen=true,sscofpmf=true,ssccfg=true,smcdeleg=true,smcsrind=true,sscsrind=true"

This makes the qemu command line quite tedious. In stead of that, I think we
can enable these features by default if there is no objection.

The first 2 patches decouple the indirect CSR usage from AIA implementation
while patch3 adds stateen bits validation for AIA.
The PATCH4 implements indirect CSR extensions while remaining patches
implement the counter delegation extensions.

The Qemu patches can be found here:
https://github.com/atishp04/qemu/tree/counter_delegation_rfc

The opensbi patch can be found here:
https://github.com/atishp04/opensbi/tree/counter_delegation_v1

The Linux kernel patches can be found here:
https://github.com/atishp04/linux/tree/counter_delegation_rfc

[1] https://github.com/riscv/riscv-indirect-csr-access
[2] https://github.com/riscv/riscv-smcdeleg-ssccfg

Atish Patra (1):
target/riscv: Enable S*stateen bits for AIA

Kaiwen Xue (7):
target/riscv: Add properties for Indirect CSR Access extension
target/riscv: Decouple AIA processing from xiselect and xireg
target/riscv: Support generic CSR indirect access
target/riscv: Add smcdeleg/ssccfg properties
target/riscv: Add counter delegation definitions
target/riscv: Add select value range check for counter delegation
target/riscv: Add counter delegation/configuration support

target/riscv/cpu.c  |   8 +
target/riscv/cpu.h  |   1 +
target/riscv/cpu_bits.h |  34 +-
target/riscv/cpu_cfg.h  |   4 +
target/riscv/csr.c  | 713 +---
target/riscv/machine.c  |   1 +
6 files changed, 722 insertions(+), 39 deletions(-)

--
2.34.1




Re: [PATCH v2 3/3] target/riscv/translate.c: set vstart_eq_zero in mark_vs_dirty()

2024-02-16 Thread Richard Henderson

On 2/16/24 12:40, Daniel Henrique Barboza wrote:

After reading the reviews of patches 1 and 3 what I'm considering here is:

1 - drop patch 1;


Ok.


2 - there's a patch from Ivan Klokov sent 2 months ago:

"[PATCH 1/1] target/riscv: Clear vstart_qe_zero flag"
https://lore.kernel.org/qemu-riscv/20231214111851.142532-1-ivan.klo...@syntacore.com/

His patch is closer to what you suggested than mine. He already renamed 
mark_vs_dirty()
to finalize_rvv_inst() and made it set start_eq_zero unconditionally. It needs a
little work (i.e. remove the ifds from the function) that I'll do myself.

3 - I'll keep patch 2 to reduce the redundant calls to the now 
finalize_rvv_inst();


Ok.


4 - Add another patch to through all "gen_set_label(over)" cond branches and set
vstart = 0 && vstart_eq_zero manually when we're doing the jump.

In fact, shouldn't we return earlier if we're not taking the branch? Otherwise
we'll set vstart twice in case we didn't get the branch. E.g:

   TCGLabel *over = gen_new_label();
   tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
   (...)
   finalize_rvv_insn();
   return true;

   gen_set_label(over);
   /* some TCG ops to set cpu_vstart to zero. Perhaps a helper?  */
   s->vstart_eq_zero = true;
   return true;


That will break, of course, because you wouldn't emit 'over'.
You really need to get translation-time and run-time separate in your head.

That said, I think these brcond(vstart >= vl) are a mistake.
The loops within the helpers are generally of the form

for (i = env->vstart; i < evl; i++, env->vstart++) {

which will operate just fine with vstart >= vl, iterating zero times.
We will then fall through to the post-insn cleanup,

env->vstart = 0;
vext_set_tail_elems_1s(evl, vd, desc, nf, esz, max_elems);

or whatever.

I would expect the condition vstart >= vl to never happen in practice.  I believe the only 
way to induce it is an explicit write to vstart.  Therefore I think you should not attempt 
to "optimize away" the call to the helper.


Of course you will want to double-check all of the loop iterations in the associated 
helpers when removing the branches.



r~


PS: I believe a better form for the ldst loops is

for (i = env->vstart; i < evl; env->vstart = ++i)

to avoid re-reading from vstart each iteration.



Re: [PATCH v3 1/3] hw/i2c: core: Add reset

2024-02-16 Thread Joe Komlodi
On Thu, Feb 8, 2024 at 8:39 AM Peter Maydell  wrote:
>
> On Fri, 2 Feb 2024 at 20:48, Joe Komlodi  wrote:
> >
> > It's possible for a reset to come in the middle of a transaction, which
> > causes the bus to be in an old state when a new transaction comes in.
> >
> > Signed-off-by: Joe Komlodi 
> > ---
> >  hw/i2c/core.c| 19 +++
> >  include/hw/i2c/i2c.h |  2 +-
> >  2 files changed, 20 insertions(+), 1 deletion(-)
> >
> > diff --git a/hw/i2c/core.c b/hw/i2c/core.c
> > index 4cf30b2c86..3128067bba 100644
> > --- a/hw/i2c/core.c
> > +++ b/hw/i2c/core.c
> > @@ -23,10 +23,29 @@ static Property i2c_props[] = {
> >  DEFINE_PROP_END_OF_LIST(),
> >  };
> >
> > +static void i2c_bus_hold_reset(Object *obj)
> > +{
> > +I2CBus *bus = I2C_BUS(obj);
> > +I2CNode *node, *next;
> > +
> > +bus->broadcast = false;
> > +QLIST_FOREACH_SAFE(node, >current_devs, next, next) {
> > +QLIST_REMOVE(node, next);
> > +g_free(node);
> > +}
> > +}
>
> This does what it says it's going to do; but I think it
> would be good to hear from Corey whether it's better to
> do this, or instead to call i2c_end_transfer() in the
> reset-enter phase.

i2c_end_transfer() might actually make more sense (explained a little
more below). I'll see what Corey says though.
>
> Mostly QEMU's "reset" is like power-cycling, in which case
> I guess that what we have here where we just forget about
> the in-progress transfer and assume the device on the other
> end is also going to reset back to a neutral state is what
> we want.
>
> Does i2c have a concept of a bus-level "reset" operation?
>
Not really, as far as I know.
On hardware I believe if a reset happened in the middle of a
transaction it would just look like a transaction ending from the
target's PoV.

> > +
> > +static void i2c_bus_class_init(ObjectClass *klass, void *data)
> > +{
> > +ResettableClass *rc = RESETTABLE_CLASS(klass);
> > +rc->phases.hold = i2c_bus_hold_reset;
> > +}
> > +
> >  static const TypeInfo i2c_bus_info = {
> >  .name = TYPE_I2C_BUS,
> >  .parent = TYPE_BUS,
> >  .instance_size = sizeof(I2CBus),
> > +.class_init = i2c_bus_class_init,
> >  };
>
>
>
> >  static int i2c_bus_pre_save(void *opaque)
> > diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h
> > index 2a3abacd1b..49580e30e2 100644
> > --- a/include/hw/i2c/i2c.h
> > +++ b/include/hw/i2c/i2c.h
> > @@ -64,7 +64,7 @@ struct I2CSlave {
> >  };
> >
> >  #define TYPE_I2C_BUS "i2c-bus"
> > -OBJECT_DECLARE_SIMPLE_TYPE(I2CBus, I2C_BUS)
> > +OBJECT_DECLARE_TYPE(I2CBus, I2CBusClass, I2C_BUS)
>
> I don't think you need this change any more ?

Oops, will fix in v4. I'll hold off on sending it until Corey gives
input on the reset behavior.

Thanks,
Joe
>
> thanks
> -- PMM



[PATCH v2 7/7] Update maintainer contact for migration multifd zero page checking acceleration.

2024-02-16 Thread Hao Xiang
Add myself to maintain multifd zero page checking acceleration function.

Signed-off-by: Hao Xiang 
---
 MAINTAINERS | 5 +
 1 file changed, 5 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index a24c2b51b6..3ca407cb58 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3403,6 +3403,11 @@ F: tests/migration/
 F: util/userfaultfd.c
 X: migration/rdma*
 
+Migration multifd zero page checking acceleration
+M: Hao Xiang 
+S: Maintained
+F: migration/multifd-zero-page.c
+
 RDMA Migration
 R: Li Zhijian 
 R: Peter Xu 
-- 
2.30.2




[PATCH v2 6/7] migration/multifd: Add zero pages and zero bytes counter to migration status interface.

2024-02-16 Thread Hao Xiang
This change extends the MigrationStatus interface to track zero pages
and zero bytes counter.

Signed-off-by: Hao Xiang 
---
 migration/migration-hmp-cmds.c  |  4 
 migration/migration.c   |  2 ++
 qapi/migration.json | 15 ++-
 tests/migration/guestperf/engine.py |  2 ++
 4 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 7e96ae6ffd..abe035c9f2 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -111,6 +111,10 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
info->ram->normal);
 monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n",
info->ram->normal_bytes >> 10);
+monitor_printf(mon, "zero: %" PRIu64 " pages\n",
+   info->ram->zero);
+monitor_printf(mon, "zero bytes: %" PRIu64 " kbytes\n",
+   info->ram->zero_bytes >> 10);
 monitor_printf(mon, "dirty sync count: %" PRIu64 "\n",
info->ram->dirty_sync_count);
 monitor_printf(mon, "page size: %" PRIu64 " kbytes\n",
diff --git a/migration/migration.c b/migration/migration.c
index ab21de2cad..1968ea7075 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1112,6 +1112,8 @@ static void populate_ram_info(MigrationInfo *info, 
MigrationState *s)
 info->ram->skipped = 0;
 info->ram->normal = stat64_get(_stats.normal_pages);
 info->ram->normal_bytes = info->ram->normal * page_size;
+info->ram->zero = stat64_get(_stats.zero_pages);
+info->ram->zero_bytes = info->ram->zero * page_size;
 info->ram->mbps = s->mbps;
 info->ram->dirty_sync_count =
 stat64_get(_stats.dirty_sync_count);
diff --git a/qapi/migration.json b/qapi/migration.json
index e2450b92d4..892875da18 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -63,6 +63,10 @@
 # between 0 and @dirty-sync-count * @multifd-channels.  (since
 # 7.1)
 #
+# @zero: number of zero pages (since 9.0)
+#
+# @zero-bytes: number of zero bytes sent (since 9.0)
+#
 # Features:
 #
 # @deprecated: Member @skipped is always zero since 1.5.3
@@ -81,7 +85,8 @@
'multifd-bytes': 'uint64', 'pages-per-second': 'uint64',
'precopy-bytes': 'uint64', 'downtime-bytes': 'uint64',
'postcopy-bytes': 'uint64',
-   'dirty-sync-missed-zero-copy': 'uint64' } }
+   'dirty-sync-missed-zero-copy': 'uint64',
+   'zero': 'int', 'zero-bytes': 'int' } }
 
 ##
 # @XBZRLECacheStats:
@@ -332,6 +337,8 @@
 #   "duplicate":123,
 #   "normal":123,
 #   "normal-bytes":123456,
+#   "zero":123,
+#   "zero-bytes":123456,
 #   "dirty-sync-count":15
 # }
 #  }
@@ -358,6 +365,8 @@
 # "duplicate":123,
 # "normal":123,
 # "normal-bytes":123456,
+# "zero":123,
+# "zero-bytes":123456,
 # "dirty-sync-count":15
 #  }
 #   }
@@ -379,6 +388,8 @@
 # "duplicate":123,
 # "normal":123,
 # "normal-bytes":123456,
+# "zero":123,
+# "zero-bytes":123456,
 # "dirty-sync-count":15
 #  },
 #  "disk":{
@@ -405,6 +416,8 @@
 # "duplicate":10,
 # "normal":,
 # "normal-bytes":3412992,
+# "zero":,
+# "zero-bytes":3412992,
 # "dirty-sync-count":15
 #  },
 #  "xbzrle-cache":{
diff --git a/tests/migration/guestperf/engine.py 
b/tests/migration/guestperf/engine.py
index 608d7270f6..75315b99b7 100644
--- a/tests/migration/guestperf/engine.py
+++ b/tests/migration/guestperf/engine.py
@@ -92,6 +92,8 @@ def _migrate_progress(self, vm):
 info["ram"].get("skipped", 0),
 info["ram"].get("normal", 0),
 info["ram"].get("normal-bytes", 0),
+info["ram"].get("zero", 0);
+info["ram"].get("zero-bytes", 0);
 info["ram"].get("dirty-pages-rate", 0),
 info["ram"].get("mbps", 0),
 info["ram"].get("dirty-sync-count", 0)
-- 
2.30.2




[PATCH v2 4/7] migration/multifd: Enable zero page checking from multifd threads.

2024-02-16 Thread Hao Xiang
This change adds a dedicated handler for MigrationOps::ram_save_target_page in
multifd live migration. Now zero page checking can be done in the multifd 
threads
and this becomes the default configuration. We still provide backward 
compatibility
where zero page checking is done from the migration main thread.

Signed-off-by: Hao Xiang 
---
 migration/multifd.c |  1 +
 migration/options.c |  2 +-
 migration/ram.c | 53 ++---
 3 files changed, 42 insertions(+), 14 deletions(-)

diff --git a/migration/multifd.c b/migration/multifd.c
index fbb40ea10b..ef5dad1019 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -13,6 +13,7 @@
 #include "qemu/osdep.h"
 #include "qemu/cutils.h"
 #include "qemu/rcu.h"
+#include "qemu/cutils.h"
 #include "exec/target_page.h"
 #include "sysemu/sysemu.h"
 #include "exec/ramblock.h"
diff --git a/migration/options.c b/migration/options.c
index 3c603391b0..3c79b6ccd4 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -181,7 +181,7 @@ Property migration_properties[] = {
   MIG_MODE_NORMAL),
 DEFINE_PROP_ZERO_PAGE_DETECTION("zero-page-detection", MigrationState,
parameters.zero_page_detection,
-   ZERO_PAGE_DETECTION_LEGACY),
+   ZERO_PAGE_DETECTION_MULTIFD),
 
 /* Migration capabilities */
 DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
diff --git a/migration/ram.c b/migration/ram.c
index 5ece9f042e..b088c5a98c 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -1123,10 +1123,6 @@ static int save_zero_page(RAMState *rs, PageSearchStatus 
*pss,
 QEMUFile *file = pss->pss_channel;
 int len = 0;
 
-if (migrate_zero_page_detection() != ZERO_PAGE_DETECTION_LEGACY) {
-return 0;
-}
-
 if (!buffer_is_zero(p, TARGET_PAGE_SIZE)) {
 return 0;
 }
@@ -1256,6 +1252,10 @@ static int ram_save_page(RAMState *rs, PageSearchStatus 
*pss)
 
 static int ram_save_multifd_page(RAMBlock *block, ram_addr_t offset)
 {
+assert(migrate_multifd());
+assert(!migrate_compress());
+assert(!migration_in_postcopy());
+
 if (!multifd_queue_page(block, offset)) {
 return -1;
 }
@@ -2046,7 +2046,6 @@ static bool save_compress_page(RAMState *rs, 
PageSearchStatus *pss,
  */
 static int ram_save_target_page_legacy(RAMState *rs, PageSearchStatus *pss)
 {
-RAMBlock *block = pss->block;
 ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS;
 int res;
 
@@ -2062,17 +2061,40 @@ static int ram_save_target_page_legacy(RAMState *rs, 
PageSearchStatus *pss)
 return 1;
 }
 
+return ram_save_page(rs, pss);
+}
+
+/**
+ * ram_save_target_page_multifd: save one target page
+ *
+ * Returns the number of pages written
+ *
+ * @rs: current RAM state
+ * @pss: data about the page we want to send
+ */
+static int ram_save_target_page_multifd(RAMState *rs, PageSearchStatus *pss)
+{
+RAMBlock *block = pss->block;
+ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS;
+
+/* Multifd is not compatible with old compression. */
+assert(!migrate_compress());
+
+/* Multifd is not compabible with postcopy. */
+assert(!migration_in_postcopy());
+
 /*
- * Do not use multifd in postcopy as one whole host page should be
- * placed.  Meanwhile postcopy requires atomic update of pages, so even
- * if host page size == guest page size the dest guest during run may
- * still see partially copied pages which is data corruption.
+ * Backward compatibility support. While using multifd live
+ * migration, we still need to handle zero page checking on the
+ * migration main thread.
  */
-if (migrate_multifd() && !migration_in_postcopy()) {
-return ram_save_multifd_page(block, offset);
+if (migrate_zero_page_detection() == ZERO_PAGE_DETECTION_LEGACY) {
+if (save_zero_page(rs, pss, offset)) {
+return 1;
+}
 }
 
-return ram_save_page(rs, pss);
+return ram_save_multifd_page(block, offset);
 }
 
 /* Should be called before sending a host page */
@@ -2984,7 +3006,12 @@ static int ram_save_setup(QEMUFile *f, void *opaque)
 }
 
 migration_ops = g_malloc0(sizeof(MigrationOps));
-migration_ops->ram_save_target_page = ram_save_target_page_legacy;
+
+if (migrate_multifd()) {
+migration_ops->ram_save_target_page = ram_save_target_page_multifd;
+} else {
+migration_ops->ram_save_target_page = ram_save_target_page_legacy;
+}
 
 bql_unlock();
 ret = multifd_send_sync_main();
-- 
2.30.2




[PATCH v2 2/7] migration/multifd: Support for zero pages transmission in multifd format.

2024-02-16 Thread Hao Xiang
This change adds zero page counters and updates multifd send/receive
tracing format to track the newly added counters.

Signed-off-by: Hao Xiang 
---
 migration/multifd.c| 43 ++
 migration/multifd.h| 21 -
 migration/ram.c|  1 -
 migration/trace-events |  8 
 4 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/migration/multifd.c b/migration/multifd.c
index adfe8c9a0a..a33dba40d9 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -236,6 +236,8 @@ static void multifd_pages_reset(MultiFDPages_t *pages)
  * overwritten later when reused.
  */
 pages->num = 0;
+pages->normal_num = 0;
+pages->zero_num = 0;
 pages->block = NULL;
 }
 
@@ -309,6 +311,8 @@ static MultiFDPages_t *multifd_pages_init(uint32_t n)
 
 pages->allocated = n;
 pages->offset = g_new0(ram_addr_t, n);
+pages->normal = g_new0(ram_addr_t, n);
+pages->zero = g_new0(ram_addr_t, n);
 
 return pages;
 }
@@ -319,6 +323,10 @@ static void multifd_pages_clear(MultiFDPages_t *pages)
 pages->allocated = 0;
 g_free(pages->offset);
 pages->offset = NULL;
+g_free(pages->normal);
+pages->normal = NULL;
+g_free(pages->zero);
+pages->zero = NULL;
 g_free(pages);
 }
 
@@ -332,6 +340,7 @@ void multifd_send_fill_packet(MultiFDSendParams *p)
 packet->flags = cpu_to_be32(p->flags);
 packet->pages_alloc = cpu_to_be32(p->pages->allocated);
 packet->normal_pages = cpu_to_be32(pages->num);
+packet->zero_pages = cpu_to_be32(pages->zero_num);
 packet->next_packet_size = cpu_to_be32(p->next_packet_size);
 
 packet_num = qatomic_fetch_inc(_send_state->packet_num);
@@ -350,9 +359,10 @@ void multifd_send_fill_packet(MultiFDSendParams *p)
 
 p->packets_sent++;
 p->total_normal_pages += pages->num;
+p->total_zero_pages += pages->zero_num;
 
-trace_multifd_send(p->id, packet_num, pages->num, p->flags,
-   p->next_packet_size);
+trace_multifd_send(p->id, packet_num, pages->num, pages->zero_num,
+   p->flags, p->next_packet_size);
 }
 
 static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp)
@@ -393,20 +403,29 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams 
*p, Error **errp)
 p->normal_num = be32_to_cpu(packet->normal_pages);
 if (p->normal_num > packet->pages_alloc) {
 error_setg(errp, "multifd: received packet "
-   "with %u pages and expected maximum pages are %u",
+   "with %u normal pages and expected maximum pages are %u",
p->normal_num, packet->pages_alloc) ;
 return -1;
 }
 
+p->zero_num = be32_to_cpu(packet->zero_pages);
+if (p->zero_num > packet->pages_alloc - p->normal_num) {
+error_setg(errp, "multifd: received packet "
+   "with %u zero pages and expected maximum zero pages are %u",
+   p->zero_num, packet->pages_alloc - p->normal_num) ;
+return -1;
+}
+
 p->next_packet_size = be32_to_cpu(packet->next_packet_size);
 p->packet_num = be64_to_cpu(packet->packet_num);
 p->packets_recved++;
 p->total_normal_pages += p->normal_num;
+p->total_zero_pages += p->zero_num;
 
-trace_multifd_recv(p->id, p->packet_num, p->normal_num, p->flags,
-   p->next_packet_size);
+trace_multifd_recv(p->id, p->packet_num, p->normal_num, p->zero_num,
+   p->flags, p->next_packet_size);
 
-if (p->normal_num == 0) {
+if (p->normal_num == 0 && p->zero_num == 0) {
 return 0;
 }
 
@@ -823,6 +842,8 @@ static void *multifd_send_thread(void *opaque)
 
 stat64_add(_stats.multifd_bytes,
p->next_packet_size + p->packet_len);
+stat64_add(_stats.normal_pages, pages->num);
+stat64_add(_stats.zero_pages, pages->zero_num);
 
 multifd_pages_reset(p->pages);
 p->next_packet_size = 0;
@@ -866,7 +887,8 @@ out:
 
 rcu_unregister_thread();
 migration_threads_remove(thread);
-trace_multifd_send_thread_end(p->id, p->packets_sent, 
p->total_normal_pages);
+trace_multifd_send_thread_end(p->id, p->packets_sent, 
p->total_normal_pages,
+  p->total_zero_pages);
 
 return NULL;
 }
@@ -1132,6 +1154,8 @@ static void 
multifd_recv_cleanup_channel(MultiFDRecvParams *p)
 p->iov = NULL;
 g_free(p->normal);
 p->normal = NULL;
+g_free(p->zero);
+p->zero = NULL;
 multifd_recv_state->ops->recv_cleanup(p);
 }
 
@@ -1251,7 +1275,9 @@ static void *multifd_recv_thread(void *opaque)
 }
 
 rcu_unregister_thread();
-trace_multifd_recv_thread_end(p->id, p->packets_recved, 
p->total_normal_pages);
+trace_multifd_recv_thread_end(p->id, p->packets_recved,
+  p->total_normal_pages,
+  

[PATCH v2 0/7] Introduce multifd zero page checking.

2024-02-16 Thread Hao Xiang
v2 update:
* Implement zero-page-detection switch with enumeration "legacy",
"none" and "multifd".
* Move normal/zero pages from MultiFDSendParams to MultiFDPages_t.
* Add zeros and zero_bytes accounting.

This patchset is based on Juan Quintela's old series here
https://lore.kernel.org/all/20220802063907.18882-1-quint...@redhat.com/

In the multifd live migration model, there is a single migration main
thread scanning the page map, queuing the pages to multiple multifd
sender threads. The migration main thread runs zero page checking on
every page before queuing the page to the sender threads. Zero page
checking is a CPU intensive task and hence having a single thread doing
all that doesn't scale well. This change introduces a new function
to run the zero page checking on the multifd sender threads. This
patchset also lays the ground work for future changes to offload zero
page checking task to accelerator hardwares.

Use two Intel 4th generation Xeon servers for testing.

Architecture:x86_64
CPU(s):  192
Thread(s) per core:  2
Core(s) per socket:  48
Socket(s):   2
NUMA node(s):2
Vendor ID:   GenuineIntel
CPU family:  6
Model:   143
Model name:  Intel(R) Xeon(R) Platinum 8457C
Stepping:8
CPU MHz: 2538.624
CPU max MHz: 3800.
CPU min MHz: 800.

Perform multifd live migration with below setup:
1. VM has 100GB memory. All pages in the VM are zero pages.
2. Use tcp socket for live migration.
3. Use 4 multifd channels and zero page checking on migration main thread.
4. Use 1/2/4 multifd channels and zero page checking on multifd sender
threads.
5. Record migration total time from sender QEMU console's "info migrate"
command.

++
|zero-page-checking | total-time(ms) |
++
|main-thread| 9629   |
++
|multifd-1-threads  | 6182   |
++
|multifd-2-threads  | 4643   |
++
|multifd-4-threads  | 4143   |
++

Apply this patchset on top of commit
5767815218efd3cbfd409505ed824d5f356044ae

Hao Xiang (7):
  migration/multifd: Add new migration option zero-page-detection.
  migration/multifd: Support for zero pages transmission in multifd
format.
  migration/multifd: Zero page transmission on the multifd thread.
  migration/multifd: Enable zero page checking from multifd threads.
  migration/multifd: Add new migration test cases for legacy zero page
checking.
  migration/multifd: Add zero pages and zero bytes counter to migration
status interface.
  Update maintainer contact for migration multifd zero page checking
acceleration.

 MAINTAINERS |  5 ++
 hw/core/qdev-properties-system.c| 10 
 include/hw/qdev-properties-system.h |  4 ++
 migration/meson.build   |  1 +
 migration/migration-hmp-cmds.c  | 13 +
 migration/migration.c   |  2 +
 migration/multifd-zero-page.c   | 59 +++
 migration/multifd-zlib.c| 26 +++--
 migration/multifd-zstd.c| 25 ++--
 migration/multifd.c | 90 -
 migration/multifd.h | 28 -
 migration/options.c | 21 +++
 migration/options.h |  1 +
 migration/ram.c | 50 
 migration/trace-events  |  8 +--
 qapi/migration.json | 47 +--
 tests/migration/guestperf/engine.py |  2 +
 tests/qtest/migration-test.c| 52 +
 18 files changed, 399 insertions(+), 45 deletions(-)
 create mode 100644 migration/multifd-zero-page.c

-- 
2.30.2




[PATCH v2 3/7] migration/multifd: Zero page transmission on the multifd thread.

2024-02-16 Thread Hao Xiang
1. Implements the zero page detection and handling on the multifd
threads for non-compression, zlib and zstd compression backends.
2. Added a new value 'multifd' in ZeroPageDetection enumeration.
3. Add proper asserts to ensure pages->normal are used for normal pages
in all scenarios.

Signed-off-by: Hao Xiang 
---
 migration/meson.build |  1 +
 migration/multifd-zero-page.c | 59 +++
 migration/multifd-zlib.c  | 26 ---
 migration/multifd-zstd.c  | 25 ---
 migration/multifd.c   | 50 +++--
 migration/multifd.h   |  7 +
 qapi/migration.json   |  4 ++-
 7 files changed, 151 insertions(+), 21 deletions(-)
 create mode 100644 migration/multifd-zero-page.c

diff --git a/migration/meson.build b/migration/meson.build
index 92b1cc4297..1eeb915ff6 100644
--- a/migration/meson.build
+++ b/migration/meson.build
@@ -22,6 +22,7 @@ system_ss.add(files(
   'migration.c',
   'multifd.c',
   'multifd-zlib.c',
+  'multifd-zero-page.c',
   'ram-compress.c',
   'options.c',
   'postcopy-ram.c',
diff --git a/migration/multifd-zero-page.c b/migration/multifd-zero-page.c
new file mode 100644
index 00..f0cd8e2c53
--- /dev/null
+++ b/migration/multifd-zero-page.c
@@ -0,0 +1,59 @@
+/*
+ * Multifd zero page detection implementation.
+ *
+ * Copyright (c) 2024 Bytedance Inc
+ *
+ * Authors:
+ *  Hao Xiang 
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "exec/ramblock.h"
+#include "migration.h"
+#include "multifd.h"
+#include "options.h"
+#include "ram.h"
+
+void multifd_zero_page_check_send(MultiFDSendParams *p)
+{
+/*
+ * QEMU older than 9.0 don't understand zero page
+ * on multifd channel. This switch is required to
+ * maintain backward compatibility.
+ */
+bool use_multifd_zero_page =
+(migrate_zero_page_detection() == ZERO_PAGE_DETECTION_MULTIFD);
+MultiFDPages_t *pages = p->pages;
+RAMBlock *rb = pages->block;
+
+assert(pages->num != 0);
+assert(pages->normal_num == 0);
+assert(pages->zero_num == 0);
+
+for (int i = 0; i < pages->num; i++) {
+uint64_t offset = pages->offset[i];
+if (use_multifd_zero_page &&
+buffer_is_zero(rb->host + offset, p->page_size)) {
+pages->zero[pages->zero_num] = offset;
+pages->zero_num++;
+ram_release_page(rb->idstr, offset);
+} else {
+pages->normal[pages->normal_num] = offset;
+pages->normal_num++;
+}
+}
+}
+
+void multifd_zero_page_check_recv(MultiFDRecvParams *p)
+{
+for (int i = 0; i < p->zero_num; i++) {
+void *page = p->host + p->zero[i];
+if (!buffer_is_zero(page, p->page_size)) {
+memset(page, 0, p->page_size);
+}
+}
+}
diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c
index 012e3bdea1..cdfe0fa70e 100644
--- a/migration/multifd-zlib.c
+++ b/migration/multifd-zlib.c
@@ -123,13 +123,20 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error 
**errp)
 int ret;
 uint32_t i;
 
+multifd_zero_page_check_send(p);
+
+if (!pages->normal_num) {
+p->next_packet_size = 0;
+goto out;
+}
+
 multifd_send_prepare_header(p);
 
-for (i = 0; i < pages->num; i++) {
+for (i = 0; i < pages->normal_num; i++) {
 uint32_t available = z->zbuff_len - out_size;
 int flush = Z_NO_FLUSH;
 
-if (i == pages->num - 1) {
+if (i == pages->normal_num - 1) {
 flush = Z_SYNC_FLUSH;
 }
 
@@ -138,7 +145,7 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error 
**errp)
  * with compression. zlib does not guarantee that this is safe,
  * therefore copy the page before calling deflate().
  */
-memcpy(z->buf, p->pages->block->host + pages->offset[i], p->page_size);
+memcpy(z->buf, p->pages->block->host + pages->normal[i], p->page_size);
 zs->avail_in = p->page_size;
 zs->next_in = z->buf;
 
@@ -172,10 +179,10 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error 
**errp)
 p->iov[p->iovs_num].iov_len = out_size;
 p->iovs_num++;
 p->next_packet_size = out_size;
-p->flags |= MULTIFD_FLAG_ZLIB;
 
+out:
+p->flags |= MULTIFD_FLAG_ZLIB;
 multifd_send_fill_packet(p);
-
 return 0;
 }
 
@@ -261,6 +268,14 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error 
**errp)
p->id, flags, MULTIFD_FLAG_ZLIB);
 return -1;
 }
+
+multifd_zero_page_check_recv(p);
+
+if (!p->normal_num) {
+assert(in_size == 0);
+return 0;
+}
+
 ret = qio_channel_read_all(p->c, (void *)z->zbuff, in_size, errp);
 
 if (ret != 0) {
@@ -310,6 +325,7 @@ static int zlib_recv_pages(MultiFDRecvParams *p, Error 

[PATCH v2 5/7] migration/multifd: Add new migration test cases for legacy zero page checking.

2024-02-16 Thread Hao Xiang
Now that zero page checking is done on the multifd sender threads by
default, we still provide an option for backward compatibility. This
change adds a qtest migration test case to set the zero-page-detection
option to "legacy" and run multifd migration with zero page checking on the
migration main thread.

Signed-off-by: Hao Xiang 
---
 tests/qtest/migration-test.c | 52 
 1 file changed, 52 insertions(+)

diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 8a5bb1752e..c27083110a 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -2621,6 +2621,24 @@ test_migrate_precopy_tcp_multifd_start(QTestState *from,
 return test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
 }
 
+static void *
+test_migrate_precopy_tcp_multifd_start_zero_page_legacy(QTestState *from,
+QTestState *to)
+{
+test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
+migrate_set_parameter_str(from, "zero-page-detection", "legacy");
+return NULL;
+}
+
+static void *
+test_migration_precopy_tcp_multifd_start_no_zero_page(QTestState *from,
+  QTestState *to)
+{
+test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
+migrate_set_parameter_str(from, "zero-page-detection", "none");
+return NULL;
+}
+
 static void *
 test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from,
 QTestState *to)
@@ -2652,6 +2670,36 @@ static void test_multifd_tcp_none(void)
 test_precopy_common();
 }
 
+static void test_multifd_tcp_zero_page_legacy(void)
+{
+MigrateCommon args = {
+.listen_uri = "defer",
+.start_hook = test_migrate_precopy_tcp_multifd_start_zero_page_legacy,
+/*
+ * Multifd is more complicated than most of the features, it
+ * directly takes guest page buffers when sending, make sure
+ * everything will work alright even if guest page is changing.
+ */
+.live = true,
+};
+test_precopy_common();
+}
+
+static void test_multifd_tcp_no_zero_page(void)
+{
+MigrateCommon args = {
+.listen_uri = "defer",
+.start_hook = test_migration_precopy_tcp_multifd_start_no_zero_page,
+/*
+ * Multifd is more complicated than most of the features, it
+ * directly takes guest page buffers when sending, make sure
+ * everything will work alright even if guest page is changing.
+ */
+.live = true,
+};
+test_precopy_common();
+}
+
 static void test_multifd_tcp_zlib(void)
 {
 MigrateCommon args = {
@@ -3550,6 +3598,10 @@ int main(int argc, char **argv)
 }
 migration_test_add("/migration/multifd/tcp/plain/none",
test_multifd_tcp_none);
+migration_test_add("/migration/multifd/tcp/plain/zero_page_legacy",
+   test_multifd_tcp_zero_page_legacy);
+migration_test_add("/migration/multifd/tcp/plain/no_zero_page",
+   test_multifd_tcp_no_zero_page);
 migration_test_add("/migration/multifd/tcp/plain/cancel",
test_multifd_tcp_cancel);
 migration_test_add("/migration/multifd/tcp/plain/zlib",
-- 
2.30.2




[PATCH v2 1/7] migration/multifd: Add new migration option zero-page-detection.

2024-02-16 Thread Hao Xiang
This new parameter controls where the zero page checking is running.
1. If this parameter is set to 'legacy', zero page checking is
done in the migration main thread.
2. If this parameter is set to 'none', zero page checking is disabled.

Signed-off-by: Hao Xiang 
---
 hw/core/qdev-properties-system.c| 10 ++
 include/hw/qdev-properties-system.h |  4 
 migration/migration-hmp-cmds.c  |  9 +
 migration/options.c | 21 
 migration/options.h |  1 +
 migration/ram.c |  4 
 qapi/migration.json | 30 ++---
 7 files changed, 76 insertions(+), 3 deletions(-)

diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 1a396521d5..63843f18b5 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -679,6 +679,16 @@ const PropertyInfo qdev_prop_mig_mode = {
 .set_default_value = qdev_propinfo_set_default_value_enum,
 };
 
+const PropertyInfo qdev_prop_zero_page_detection = {
+.name = "ZeroPageDetection",
+.description = "zero_page_detection values, "
+   "multifd,legacy,none",
+.enum_table = _lookup,
+.get = qdev_propinfo_get_enum,
+.set = qdev_propinfo_set_enum,
+.set_default_value = qdev_propinfo_set_default_value_enum,
+};
+
 /* --- Reserved Region --- */
 
 /*
diff --git a/include/hw/qdev-properties-system.h 
b/include/hw/qdev-properties-system.h
index 06c359c190..839b170235 100644
--- a/include/hw/qdev-properties-system.h
+++ b/include/hw/qdev-properties-system.h
@@ -8,6 +8,7 @@ extern const PropertyInfo qdev_prop_macaddr;
 extern const PropertyInfo qdev_prop_reserved_region;
 extern const PropertyInfo qdev_prop_multifd_compression;
 extern const PropertyInfo qdev_prop_mig_mode;
+extern const PropertyInfo qdev_prop_zero_page_detection;
 extern const PropertyInfo qdev_prop_losttickpolicy;
 extern const PropertyInfo qdev_prop_blockdev_on_error;
 extern const PropertyInfo qdev_prop_bios_chs_trans;
@@ -47,6 +48,9 @@ extern const PropertyInfo qdev_prop_iothread_vq_mapping_list;
 #define DEFINE_PROP_MIG_MODE(_n, _s, _f, _d) \
 DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_mig_mode, \
MigMode)
+#define DEFINE_PROP_ZERO_PAGE_DETECTION(_n, _s, _f, _d) \
+DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_zero_page_detection, \
+   ZeroPageDetection)
 #define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \
 DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_losttickpolicy, \
 LostTickPolicy)
diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 99b49df5dd..7e96ae6ffd 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -344,6 +344,11 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict 
*qdict)
 monitor_printf(mon, "%s: %s\n",
 MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION),
 MultiFDCompression_str(params->multifd_compression));
+assert(params->has_zero_page_detection);
+monitor_printf(mon, "%s: %s\n",
+MigrationParameter_str(MIGRATION_PARAMETER_ZERO_PAGE_DETECTION),
+qapi_enum_lookup(_lookup,
+params->zero_page_detection));
 monitor_printf(mon, "%s: %" PRIu64 " bytes\n",
 MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE),
 params->xbzrle_cache_size);
@@ -634,6 +639,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict 
*qdict)
 p->has_multifd_zstd_level = true;
 visit_type_uint8(v, param, >multifd_zstd_level, );
 break;
+case MIGRATION_PARAMETER_ZERO_PAGE_DETECTION:
+p->has_zero_page_detection = true;
+visit_type_ZeroPageDetection(v, param, >zero_page_detection, );
+break;
 case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE:
 p->has_xbzrle_cache_size = true;
 if (!visit_type_size(v, param, _size, )) {
diff --git a/migration/options.c b/migration/options.c
index 3e3e0b93b4..3c603391b0 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -179,6 +179,9 @@ Property migration_properties[] = {
 DEFINE_PROP_MIG_MODE("mode", MigrationState,
   parameters.mode,
   MIG_MODE_NORMAL),
+DEFINE_PROP_ZERO_PAGE_DETECTION("zero-page-detection", MigrationState,
+   parameters.zero_page_detection,
+   ZERO_PAGE_DETECTION_LEGACY),
 
 /* Migration capabilities */
 DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
@@ -903,6 +906,13 @@ uint64_t migrate_xbzrle_cache_size(void)
 return s->parameters.xbzrle_cache_size;
 }
 
+ZeroPageDetection migrate_zero_page_detection(void)
+{
+MigrationState *s = migrate_get_current();
+
+return s->parameters.zero_page_detection;
+}
+
 /* parameter setters */
 
 void 

Re: [PATCH v2 3/3] target/riscv/translate.c: set vstart_eq_zero in mark_vs_dirty()

2024-02-16 Thread Daniel Henrique Barboza




On 2/16/24 15:56, Richard Henderson wrote:

On 2/16/24 03:57, Daniel Henrique Barboza wrote:

The 'vstart_eq_zero' flag which is used to determine if some insns, like
vector reductor operations, should SIGILL. At this moment the flag is
being updated only during cpu_get_tb_cpu_state(), at the start of each
translation block.

This cadence isn't enough and we're facing situations where a vector
instruction successfully updated 'vstart' to zero, but the flag was
still marked as 'false', resulting in a SIGILL because instructions are
checking the flag.

mark_vs_dirty() is called after any instruction changes Vector CSR
state, making it a good place to update 'vstart_eq_zero'.

Fixes: 8e1ee1fb57 ("target/riscv: rvv-1.0: add translation-time vector context 
status")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1976
Signed-off-by: Daniel Henrique Barboza 
---
  target/riscv/translate.c | 20 
  1 file changed, 20 insertions(+)

diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 177418b2b9..f9ff7b6173 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -652,6 +652,8 @@ static inline void mark_fs_dirty(DisasContext *ctx) { }
   */
  static void mark_vs_dirty(DisasContext *ctx)
  {
+    TCGLabel *vstart_zero = gen_new_label();
+    TCGLabel *done = gen_new_label();
  TCGv tmp;
  if (ctx->mstatus_vs != EXT_STATUS_DIRTY) {
@@ -669,6 +671,24 @@ static void mark_vs_dirty(DisasContext *ctx)
  tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs));
  }
  }
+
+    /*
+ * We can safely make 'vl_eq_vlmax = false' if we marked
+ * VS as dirty with non-zero 'vstart', i.e. there's a fault
+ * to be handled. If 'vstart' is zero then we should retain
+ * the existing 'vl_eq_vlmax' - it'll be recalculated on the
+ * start of the next TB or during vset{i}vl{i} (that forces a
+ * TB end).
+ */
+    tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vstart, 0, vstart_zero);
+    ctx->vstart_eq_zero = false;
+    ctx->vl_eq_vlmax = false;
+    tcg_gen_br(done);
+
+    gen_set_label(vstart_zero);
+    ctx->vstart_eq_zero = true;
+
+    gen_set_label(done);


This is very confused, apparently generating code to test vstart at runtime, 
and then set some translation time variables in the branches.

Afaik, the only way vstart != 0 is an explicit set to the CSR or exiting a load 
via exception.  Therefore you have no need to have any sort of brcond here -- 
just set
ctx->vstart_eq_zero = true.

Also, you need to move the ifdefs from around mark_vs_dirty, because it is now 
relevant to user-only.

It may be worth a rename, because it does more than mark vs dirty, and 
therefore is different than mark_fs_dirty.

You need to review all instances of

     TCGLabel *over = gen_new_label();
     tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
...
     gen_set_label(over);
     return true;

because this *should have* set vstart = 0.  With vstart < vl, this is done in 
the helper function, but every place using a conditional branch needs attention.


After reading the reviews of patches 1 and 3 what I'm considering here is:

1 - drop patch 1;

2 - there's a patch from Ivan Klokov sent 2 months ago:

"[PATCH 1/1] target/riscv: Clear vstart_qe_zero flag"
https://lore.kernel.org/qemu-riscv/20231214111851.142532-1-ivan.klo...@syntacore.com/

His patch is closer to what you suggested than mine. He already renamed 
mark_vs_dirty()
to finalize_rvv_inst() and made it set start_eq_zero unconditionally. It needs a
little work (i.e. remove the ifds from the function) that I'll do myself.

3 - I'll keep patch 2 to reduce the redundant calls to the now 
finalize_rvv_inst();

4 - Add another patch to through all "gen_set_label(over)" cond branches and set
vstart = 0 && vstart_eq_zero manually when we're doing the jump.

In fact, shouldn't we return earlier if we're not taking the branch? Otherwise
we'll set vstart twice in case we didn't get the branch. E.g:

  TCGLabel *over = gen_new_label();
  tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
  (...)
  finalize_rvv_insn();
  return true;

  gen_set_label(over);
  /* some TCG ops to set cpu_vstart to zero. Perhaps a helper?  */
  s->vstart_eq_zero = true;
  return true;




Thanks,

Daniel










r~




Re: [PATCH v4 00/10] Optimize buffer_is_zero

2024-02-16 Thread Richard Henderson

On 2/16/24 10:20, Alexander Monakov wrote:

FWIW, in situations like these I always recommend to run perf with fixed
sampling rate, i.e. 'perf record -e cycles:P -c 10' or 'perf record -e
cycles/period=10/P' to make sample counts between runs of different
duration directly comparable (displayed with 'perf report -n').


I've re-done the numbers with fixed period, as suggested, and the difference between v3 
and v4+ is in the sampling noise, differing about 0.3%.



r~



[PATCH] tcg/aarch64: Apple does not align __int128_t in even registers

2024-02-16 Thread Richard Henderson
Cc: qemu-sta...@nongnu.org
Fixes: 5427a9a7604 ("tcg: Add TCG_TARGET_CALL_{RET,ARG}_I128")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2169
Signed-off-by: Richard Henderson 
---

See the gitlab issue for complete discussion of the ABI.

r~

---
 tcg/aarch64/tcg-target.h | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h
index ef5ebe91bd..85d5746e47 100644
--- a/tcg/aarch64/tcg-target.h
+++ b/tcg/aarch64/tcg-target.h
@@ -55,7 +55,11 @@ typedef enum {
 #define TCG_TARGET_CALL_STACK_OFFSET0
 #define TCG_TARGET_CALL_ARG_I32 TCG_CALL_ARG_NORMAL
 #define TCG_TARGET_CALL_ARG_I64 TCG_CALL_ARG_NORMAL
-#define TCG_TARGET_CALL_ARG_I128TCG_CALL_ARG_EVEN
+#ifdef CONFIG_DARWIN
+# define TCG_TARGET_CALL_ARG_I128   TCG_CALL_ARG_NORMAL
+#else
+# define TCG_TARGET_CALL_ARG_I128   TCG_CALL_ARG_EVEN
+#endif
 #define TCG_TARGET_CALL_RET_I128TCG_CALL_RET_NORMAL
 
 #define have_lse(cpuinfo & CPUINFO_LSE)
-- 
2.34.1




[PATCH 07/13] esp.c: change esp_fifo_pop_buf() to take ESPState

2024-02-16 Thread Mark Cave-Ayland
Now that all users of esp_fifo_pop_buf() operate on the main FIFO there is no
need to pass the FIFO explicitly.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 28 ++--
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index fb2ceca36a..4d9220ab22 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -125,7 +125,7 @@ static uint8_t esp_fifo_pop(ESPState *s)
 return fifo8_pop(>fifo);
 }
 
-static uint32_t esp_fifo_pop_buf(Fifo8 *fifo, uint8_t *dest, int maxlen)
+static uint32_t esp_fifo_pop_buf(ESPState *s, uint8_t *dest, int maxlen)
 {
 const uint8_t *buf;
 uint32_t n, n2;
@@ -136,16 +136,16 @@ static uint32_t esp_fifo_pop_buf(Fifo8 *fifo, uint8_t 
*dest, int maxlen)
 }
 
 len = maxlen;
-buf = fifo8_pop_buf(fifo, len, );
+buf = fifo8_pop_buf(>fifo, len, );
 if (dest) {
 memcpy(dest, buf, n);
 }
 
 /* Add FIFO wraparound if needed */
 len -= n;
-len = MIN(len, fifo8_num_used(fifo));
+len = MIN(len, fifo8_num_used(>fifo));
 if (len) {
-buf = fifo8_pop_buf(fifo, len, );
+buf = fifo8_pop_buf(>fifo, len, );
 if (dest) {
 memcpy([n], buf, n2);
 }
@@ -459,7 +459,7 @@ static void esp_do_dma(ESPState *s)
 s->dma_memory_read(s->dma_opaque, buf, len);
 esp_set_tc(s, esp_get_tc(s) - len);
 } else {
-len = esp_fifo_pop_buf(>fifo, buf, fifo8_num_used(>fifo));
+len = esp_fifo_pop_buf(s, buf, fifo8_num_used(>fifo));
 len = MIN(fifo8_num_free(>cmdfifo), len);
 esp_raise_drq(s);
 }
@@ -515,7 +515,7 @@ static void esp_do_dma(ESPState *s)
 fifo8_push_all(>cmdfifo, buf, len);
 esp_set_tc(s, esp_get_tc(s) - len);
 } else {
-len = esp_fifo_pop_buf(>fifo, buf, fifo8_num_used(>fifo));
+len = esp_fifo_pop_buf(s, buf, fifo8_num_used(>fifo));
 len = MIN(fifo8_num_free(>cmdfifo), len);
 fifo8_push_all(>cmdfifo, buf, len);
 esp_raise_drq(s);
@@ -549,7 +549,7 @@ static void esp_do_dma(ESPState *s)
 /* Copy FIFO data to device */
 len = MIN(s->async_len, ESP_FIFO_SZ);
 len = MIN(len, fifo8_num_used(>fifo));
-len = esp_fifo_pop_buf(>fifo, s->async_buf, len);
+len = esp_fifo_pop_buf(s, s->async_buf, len);
 esp_raise_drq(s);
 }
 
@@ -713,7 +713,7 @@ static void esp_nodma_ti_dataout(ESPState *s)
 }
 len = MIN(s->async_len, ESP_FIFO_SZ);
 len = MIN(len, fifo8_num_used(>fifo));
-esp_fifo_pop_buf(>fifo, s->async_buf, len);
+esp_fifo_pop_buf(s, s->async_buf, len);
 s->async_buf += len;
 s->async_len -= len;
 s->ti_size += len;
@@ -738,7 +738,7 @@ static void esp_do_nodma(ESPState *s)
 switch (s->rregs[ESP_CMD]) {
 case CMD_SELATN:
 /* Copy FIFO into cmdfifo */
-len = esp_fifo_pop_buf(>fifo, buf, fifo8_num_used(>fifo));
+len = esp_fifo_pop_buf(s, buf, fifo8_num_used(>fifo));
 len = MIN(fifo8_num_free(>cmdfifo), len);
 fifo8_push_all(>cmdfifo, buf, len);
 
@@ -757,7 +757,7 @@ static void esp_do_nodma(ESPState *s)
 
 case CMD_SELATNS:
 /* Copy one byte from FIFO into cmdfifo */
-len = esp_fifo_pop_buf(>fifo, buf, 1);
+len = esp_fifo_pop_buf(s, buf, 1);
 len = MIN(fifo8_num_free(>cmdfifo), len);
 fifo8_push_all(>cmdfifo, buf, len);
 
@@ -774,7 +774,7 @@ static void esp_do_nodma(ESPState *s)
 
 case CMD_TI:
 /* Copy FIFO into cmdfifo */
-len = esp_fifo_pop_buf(>fifo, buf, fifo8_num_used(>fifo));
+len = esp_fifo_pop_buf(s, buf, fifo8_num_used(>fifo));
 len = MIN(fifo8_num_free(>cmdfifo), len);
 fifo8_push_all(>cmdfifo, buf, len);
 
@@ -792,7 +792,7 @@ static void esp_do_nodma(ESPState *s)
 switch (s->rregs[ESP_CMD]) {
 case CMD_TI:
 /* Copy FIFO into cmdfifo */
-len = esp_fifo_pop_buf(>fifo, buf, fifo8_num_used(>fifo));
+len = esp_fifo_pop_buf(s, buf, fifo8_num_used(>fifo));
 len = MIN(fifo8_num_free(>cmdfifo), len);
 fifo8_push_all(>cmdfifo, buf, len);
 
@@ -821,7 +821,7 @@ static void esp_do_nodma(ESPState *s)
 case CMD_SEL | CMD_DMA:
 case CMD_SELATN | CMD_DMA:
 /* Copy FIFO into cmdfifo */
-len = esp_fifo_pop_buf(>fifo, buf, fifo8_num_used(>fifo));
+len = esp_fifo_pop_buf(s, buf, fifo8_num_used(>fifo));
 len = MIN(fifo8_num_free(>cmdfifo), len);
 fifo8_push_all(>cmdfifo, buf, len);
 
@@ -836,7 +836,7 @@ static void esp_do_nodma(ESPState *s)
 case CMD_SEL:
 case CMD_SELATN:
 /* FIFO already contain entire CDB: copy to cmdfifo and execute */
-len = 

[PATCH 09/13] esp.c: move esp_set_phase() and esp_get_phase() towards the beginning of the file

2024-02-16 Thread Mark Cave-Ayland
This allows these functions to be used earlier in the file without needing a
separate forward declaration.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 36 ++--
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 6b7a972947..96aa576601 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -79,6 +79,24 @@ static void esp_lower_drq(ESPState *s)
 }
 }
 
+static const char *esp_phase_names[8] = {
+"DATA OUT", "DATA IN", "COMMAND", "STATUS",
+"(reserved)", "(reserved)", "MESSAGE OUT", "MESSAGE IN"
+};
+
+static void esp_set_phase(ESPState *s, uint8_t phase)
+{
+s->rregs[ESP_RSTAT] &= ~7;
+s->rregs[ESP_RSTAT] |= phase;
+
+trace_esp_set_phase(esp_phase_names[phase]);
+}
+
+static uint8_t esp_get_phase(ESPState *s)
+{
+return s->rregs[ESP_RSTAT] & 7;
+}
+
 void esp_dma_enable(ESPState *s, int irq, int level)
 {
 if (level) {
@@ -195,24 +213,6 @@ static uint32_t esp_get_stc(ESPState *s)
 return dmalen;
 }
 
-static const char *esp_phase_names[8] = {
-"DATA OUT", "DATA IN", "COMMAND", "STATUS",
-"(reserved)", "(reserved)", "MESSAGE OUT", "MESSAGE IN"
-};
-
-static void esp_set_phase(ESPState *s, uint8_t phase)
-{
-s->rregs[ESP_RSTAT] &= ~7;
-s->rregs[ESP_RSTAT] |= phase;
-
-trace_esp_set_phase(esp_phase_names[phase]);
-}
-
-static uint8_t esp_get_phase(ESPState *s)
-{
-return s->rregs[ESP_RSTAT] & 7;
-}
-
 static uint8_t esp_pdma_read(ESPState *s)
 {
 uint8_t val;
-- 
2.39.2




[PATCH 03/13] esp.c: replace cmdfifo use of esp_fifo_pop() in do_message_phase()

2024-02-16 Thread Mark Cave-Ayland
Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 100560244b..7a24515bb9 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -312,7 +312,8 @@ static void do_message_phase(ESPState *s)
 uint32_t n;
 
 if (s->cmdfifo_cdb_offset) {
-uint8_t message = esp_fifo_pop(>cmdfifo);
+uint8_t message = fifo8_is_empty(>cmdfifo) ? 0 :
+  fifo8_pop(>cmdfifo);
 
 trace_esp_do_identify(message);
 s->lun = message & 7;
-- 
2.39.2




[PATCH 05/13] esp.c: change esp_fifo_pop() to take ESPState

2024-02-16 Thread Mark Cave-Ayland
Now that all users of esp_fifo_pop() operate on the main FIFO there is no need
to pass the FIFO explicitly.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index b898e43e2b..0e42ff50e7 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -116,13 +116,13 @@ static void esp_fifo_push(ESPState *s, uint8_t val)
 fifo8_push(>fifo, val);
 }
 
-static uint8_t esp_fifo_pop(Fifo8 *fifo)
+static uint8_t esp_fifo_pop(ESPState *s)
 {
-if (fifo8_is_empty(fifo)) {
+if (fifo8_is_empty(>fifo)) {
 return 0;
 }
 
-return fifo8_pop(fifo);
+return fifo8_pop(>fifo);
 }
 
 static uint32_t esp_fifo_pop_buf(Fifo8 *fifo, uint8_t *dest, int maxlen)
@@ -212,7 +212,7 @@ static uint8_t esp_pdma_read(ESPState *s)
 {
 uint8_t val;
 
-val = esp_fifo_pop(>fifo);
+val = esp_fifo_pop(s);
 return val;
 }
 
@@ -1184,7 +1184,7 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr)
 
 switch (saddr) {
 case ESP_FIFO:
-s->rregs[ESP_FIFO] = esp_fifo_pop(>fifo);
+s->rregs[ESP_FIFO] = esp_fifo_pop(s);
 val = s->rregs[ESP_FIFO];
 break;
 case ESP_RINTR:
-- 
2.39.2




[PATCH 00/13] esp: avoid explicit setting of DRQ within ESP state machine

2024-02-16 Thread Mark Cave-Ayland
The ESP device has a DRQ (DMA request) signal that is used to handle flow 
control
during DMA transfers. At the moment the DRQ signal is explicitly raised and
lowered at various points in the ESP state machine as required, rather than
implementing the logic described in the datasheet:

"DREQ will remain true as long as either the FIFO contains at least one word (or
one byte if 8-bit mode) to send to memory during DMA read, or has room for one 
more
word (or byte if 8-bit mode) in the FIFO during DMA write."

This series updates the ESP device to use the same logic as described above 
which
also fixes a couple of outstanding GitLab issues related to recovery handling
during FIFO overflow on 68k Macs using PDMA.

Patches 1-3 update existing users of esp_fifo_pop_buf() and esp_fifo_pop() with
the internal cmdfifo to use the underlying Fifo8 device directly. The aim is
to ensure that all the esp_fifo_*() functions only operate on the ESP's hardware
FIFO.

Patches 4-5 update esp_fifo_push() and esp_fifo_pop() to take ESPState directly
as a parameter to prevent any usage that doesn't reference the ESP hardware
FIFO.

Patch 6 ensures that any usage of fifo8_push() for the ESP hardware FIFO is
updated to use esp_fifo_push() instead.

Patch 7 updates esp_fifo_pop_buf() to take ESPState directly as a parameter
to prevent any usage that doesn't reference the ESP hardware FIFO.

Patch 8 introduces the esp_fifo_push_buf() function for pushing multiple bytes
to the ESP hardware FIFO, and updates callers to use it accordingly.

Patch 9 is just code movement which avoids the use of a forward declaration 
whilst
also making it easier to locate the mapping between ESP SCSI phases and their
names.

Patches 10-11 introduce a new esp_update_drq() function that implements the 
above
DRQ logic which is called by both esp_fifo_{push,pop}_buf().

Patch 11 updates esp_fifo_push() and esp_fifo_pop() to use the new 
esp_update_drq()
function. At this point all reads/writes to the ESP FIFO use the esp_fifo_*()
functions and will set DRQ correctly.

Patch 12 is a small update to the logic in esp_pdma_write() to ensure that
esp_fifo_push() is always called for PDMA writes to the FIFO, thereby ensuring
that esp_update_drq() remains correct even in the case of FIFO overflow.

Finally patch 13 removes all manual calls to esp_raise_drq() and esp_lower_drq()
since the DRQ signal is now updated correctly upon each FIFO read/write access.

Signed-off-by: Mark Cave-Ayland 


Mark Cave-Ayland (13):
  esp.c: replace cmdfifo use of esp_fifo_pop_buf() in do_command_phase()
  esp.c: replace cmdfifo use of esp_fifo_pop_buf() in do_message_phase()
  esp.c: replace cmdfifo use of esp_fifo_pop() in do_message_phase()
  esp.c: change esp_fifo_push() to take ESPState
  esp.c: change esp_fifo_pop() to take ESPState
  esp.c: use esp_fifo_push() instead of fifo8_push()
  esp.c: change esp_fifo_pop_buf() to take ESPState
  esp.c: introduce esp_fifo_push_buf() function for pushing to the FIFO
  esp.c: move esp_set_phase() and esp_get_phase() towards the beginning
of the file
  esp.c: introduce esp_update_drq() and update esp_fifo_{push,pop}_buf()
to use it
  esp.c: update esp_fifo_{push,pop}() to call esp_update_drq()
  esp.c: ensure esp_pdma_write() always calls esp_fifo_push()
  esp.c: remove explicit setting of DRQ within ESP state machine

 hw/scsi/esp.c | 189 --
 1 file changed, 120 insertions(+), 69 deletions(-)

-- 
2.39.2




[PATCH 12/13] esp.c: ensure esp_pdma_write() always calls esp_fifo_push()

2024-02-16 Thread Mark Cave-Ayland
This ensures that esp_update_drq() is called via esp_fifo_push() whenever the
host uses PDMA to transfer data to a SCSI device.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 10 --
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 2150cd457b..a147509885 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -276,14 +276,12 @@ static void esp_pdma_write(ESPState *s, uint8_t val)
 {
 uint32_t dmalen = esp_get_tc(s);
 
-if (dmalen == 0) {
-return;
-}
-
 esp_fifo_push(s, val);
 
-dmalen--;
-esp_set_tc(s, dmalen);
+if (dmalen && s->drq_state) {
+dmalen--;
+esp_set_tc(s, dmalen);
+}
 }
 
 static int esp_select(ESPState *s)
-- 
2.39.2




[PATCH 08/13] esp.c: introduce esp_fifo_push_buf() function for pushing to the FIFO

2024-02-16 Thread Mark Cave-Ayland
Instead of pushing data into the FIFO directly with fifo8_push_all(), add a new
esp_fifo_push_buf() function and use it accordingly.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 11 ---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 4d9220ab22..6b7a972947 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -116,6 +116,11 @@ static void esp_fifo_push(ESPState *s, uint8_t val)
 fifo8_push(>fifo, val);
 }
 
+static void esp_fifo_push_buf(ESPState *s, uint8_t *buf, int len)
+{
+fifo8_push_all(>fifo, buf, len);
+}
+
 static uint8_t esp_fifo_pop(ESPState *s)
 {
 if (fifo8_is_empty(>fifo)) {
@@ -601,7 +606,7 @@ static void esp_do_dma(ESPState *s)
 } else {
 /* Copy device data to FIFO */
 len = MIN(len, fifo8_num_free(>fifo));
-fifo8_push_all(>fifo, s->async_buf, len);
+esp_fifo_push_buf(s, s->async_buf, len);
 esp_raise_drq(s);
 }
 
@@ -650,7 +655,7 @@ static void esp_do_dma(ESPState *s)
 if (s->dma_memory_write) {
 s->dma_memory_write(s->dma_opaque, buf, len);
 } else {
-fifo8_push_all(>fifo, buf, len);
+esp_fifo_push_buf(s, buf, len);
 }
 
 esp_set_tc(s, esp_get_tc(s) - len);
@@ -685,7 +690,7 @@ static void esp_do_dma(ESPState *s)
 if (s->dma_memory_write) {
 s->dma_memory_write(s->dma_opaque, buf, len);
 } else {
-fifo8_push_all(>fifo, buf, len);
+esp_fifo_push_buf(s, buf, len);
 }
 
 esp_set_tc(s, esp_get_tc(s) - len);
-- 
2.39.2




[PATCH 06/13] esp.c: use esp_fifo_push() instead of fifo8_push()

2024-02-16 Thread Mark Cave-Ayland
There are still a few places that use fifo8_push() instead of esp_fifo_push() in
order to push a value into the FIFO. Update those places to use esp_fifo_push()
instead.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 0e42ff50e7..fb2ceca36a 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -858,7 +858,7 @@ static void esp_do_nodma(ESPState *s)
 return;
 }
 if (fifo8_is_empty(>fifo)) {
-fifo8_push(>fifo, s->async_buf[0]);
+esp_fifo_push(s, s->async_buf[0]);
 s->async_buf++;
 s->async_len--;
 s->ti_size--;
@@ -881,7 +881,7 @@ static void esp_do_nodma(ESPState *s)
 case STAT_ST:
 switch (s->rregs[ESP_CMD]) {
 case CMD_ICCS:
-fifo8_push(>fifo, s->status);
+esp_fifo_push(s, s->status);
 esp_set_phase(s, STAT_MI);
 
 /* Process any message in phase data */
@@ -893,7 +893,7 @@ static void esp_do_nodma(ESPState *s)
 case STAT_MI:
 switch (s->rregs[ESP_CMD]) {
 case CMD_ICCS:
-fifo8_push(>fifo, 0);
+esp_fifo_push(s, 0);
 
 /* Raise end of command interrupt */
 s->rregs[ESP_RINTR] |= INTR_FC;
-- 
2.39.2




[PATCH 02/13] esp.c: replace cmdfifo use of esp_fifo_pop_buf() in do_message_phase()

2024-02-16 Thread Mark Cave-Ayland
The aim is to restrict the esp_fifo_*() functions so that they only operate on
the hardware FIFO. When reading from cmdfifo in do_message_phase() use the
underlying Fifo8 functions directly.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index f8230c74b3..100560244b 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -309,6 +309,8 @@ static void do_command_phase(ESPState *s)
 
 static void do_message_phase(ESPState *s)
 {
+uint32_t n;
+
 if (s->cmdfifo_cdb_offset) {
 uint8_t message = esp_fifo_pop(>cmdfifo);
 
@@ -320,7 +322,10 @@ static void do_message_phase(ESPState *s)
 /* Ignore extended messages for now */
 if (s->cmdfifo_cdb_offset) {
 int len = MIN(s->cmdfifo_cdb_offset, fifo8_num_used(>cmdfifo));
-esp_fifo_pop_buf(>cmdfifo, NULL, len);
+
+if (len) {
+fifo8_pop_buf(>cmdfifo, len, );
+}
 s->cmdfifo_cdb_offset = 0;
 }
 }
-- 
2.39.2




[PATCH 04/13] esp.c: change esp_fifo_push() to take ESPState

2024-02-16 Thread Mark Cave-Ayland
Now that all users of esp_fifo_push() operate on the main FIFO there is no need
to pass the FIFO explicitly.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 10 +-
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 7a24515bb9..b898e43e2b 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -106,14 +106,14 @@ void esp_request_cancelled(SCSIRequest *req)
 }
 }
 
-static void esp_fifo_push(Fifo8 *fifo, uint8_t val)
+static void esp_fifo_push(ESPState *s, uint8_t val)
 {
-if (fifo8_num_used(fifo) == fifo->capacity) {
+if (fifo8_num_used(>fifo) == s->fifo.capacity) {
 trace_esp_error_fifo_overrun();
 return;
 }
 
-fifo8_push(fifo, val);
+fifo8_push(>fifo, val);
 }
 
 static uint8_t esp_fifo_pop(Fifo8 *fifo)
@@ -224,7 +224,7 @@ static void esp_pdma_write(ESPState *s, uint8_t val)
 return;
 }
 
-esp_fifo_push(>fifo, val);
+esp_fifo_push(s, val);
 
 dmalen--;
 esp_set_tc(s, dmalen);
@@ -1240,7 +1240,7 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t 
val)
 break;
 case ESP_FIFO:
 if (!fifo8_is_full(>fifo)) {
-esp_fifo_push(>fifo, val);
+esp_fifo_push(s, val);
 }
 esp_do_nodma(s);
 break;
-- 
2.39.2




[PATCH 01/13] esp.c: replace cmdfifo use of esp_fifo_pop_buf() in do_command_phase()

2024-02-16 Thread Mark Cave-Ayland
The aim is to restrict the esp_fifo_*() functions so that they only operate on
the hardware FIFO. When reading from cmdfifo in do_command_phase() use the
underlying Fifo8 functions directly.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 590ff99744..f8230c74b3 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -265,7 +265,7 @@ static void esp_do_nodma(ESPState *s);
 
 static void do_command_phase(ESPState *s)
 {
-uint32_t cmdlen;
+uint32_t cmdlen, n;
 int32_t datalen;
 SCSIDevice *current_lun;
 uint8_t buf[ESP_CMDFIFO_SZ];
@@ -275,7 +275,7 @@ static void do_command_phase(ESPState *s)
 if (!cmdlen || !s->current_dev) {
 return;
 }
-esp_fifo_pop_buf(>cmdfifo, buf, cmdlen);
+memcpy(buf, fifo8_pop_buf(>cmdfifo, cmdlen, ), cmdlen);
 
 current_lun = scsi_device_find(>bus, 0, s->current_dev->id, s->lun);
 if (!current_lun) {
-- 
2.39.2




[PATCH 13/13] esp.c: remove explicit setting of DRQ within ESP state machine

2024-02-16 Thread Mark Cave-Ayland
Now the esp_update_drq() is called for all reads/writes to the FIFO, there is
no need to manually raise and lower the DRQ signal.

Signed-off-by: Mark Cave-Ayland 
Fixes: https://gitlab.com/qemu-project/qemu/-/issues/611
Fixes: https://gitlab.com/qemu-project/qemu/-/issues/1831
---
 hw/scsi/esp.c | 9 -
 1 file changed, 9 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index a147509885..2f45bae940 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -495,7 +495,6 @@ static void esp_dma_ti_check(ESPState *s)
 if (esp_get_tc(s) == 0 && fifo8_num_used(>fifo) < 2) {
 s->rregs[ESP_RINTR] |= INTR_BS;
 esp_raise_irq(s);
-esp_lower_drq(s);
 }
 }
 
@@ -515,7 +514,6 @@ static void esp_do_dma(ESPState *s)
 } else {
 len = esp_fifo_pop_buf(s, buf, fifo8_num_used(>fifo));
 len = MIN(fifo8_num_free(>cmdfifo), len);
-esp_raise_drq(s);
 }
 
 fifo8_push_all(>cmdfifo, buf, len);
@@ -572,7 +570,6 @@ static void esp_do_dma(ESPState *s)
 len = esp_fifo_pop_buf(s, buf, fifo8_num_used(>fifo));
 len = MIN(fifo8_num_free(>cmdfifo), len);
 fifo8_push_all(>cmdfifo, buf, len);
-esp_raise_drq(s);
 }
 trace_esp_handle_ti_cmd(cmdlen);
 s->ti_size = 0;
@@ -604,7 +601,6 @@ static void esp_do_dma(ESPState *s)
 len = MIN(s->async_len, ESP_FIFO_SZ);
 len = MIN(len, fifo8_num_used(>fifo));
 len = esp_fifo_pop_buf(s, s->async_buf, len);
-esp_raise_drq(s);
 }
 
 s->async_buf += len;
@@ -656,7 +652,6 @@ static void esp_do_dma(ESPState *s)
 /* Copy device data to FIFO */
 len = MIN(len, fifo8_num_free(>fifo));
 esp_fifo_push_buf(s, s->async_buf, len);
-esp_raise_drq(s);
 }
 
 s->async_buf += len;
@@ -722,7 +717,6 @@ static void esp_do_dma(ESPState *s)
 if (fifo8_num_used(>fifo) < 2) {
 s->rregs[ESP_RINTR] |= INTR_BS;
 esp_raise_irq(s);
-esp_lower_drq(s);
 }
 break;
 }
@@ -1011,9 +1005,6 @@ void esp_command_complete(SCSIRequest *req, size_t resid)
 s->rregs[ESP_RINTR] |= INTR_BS;
 esp_raise_irq(s);
 
-/* Ensure DRQ is set correctly for TC underflow or normal completion */
-esp_dma_ti_check(s);
-
 if (s->current_req) {
 scsi_req_unref(s->current_req);
 s->current_req = NULL;
-- 
2.39.2




[PATCH 11/13] esp.c: update esp_fifo_{push, pop}() to call esp_update_drq()

2024-02-16 Thread Mark Cave-Ayland
This ensures that the DRQ line is always set correctly when reading/writing
single bytes to/from the FIFO.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 14 ++
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index ca0fa5098d..2150cd457b 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -170,10 +170,11 @@ static void esp_fifo_push(ESPState *s, uint8_t val)
 {
 if (fifo8_num_used(>fifo) == s->fifo.capacity) {
 trace_esp_error_fifo_overrun();
-return;
+} else {
+fifo8_push(>fifo, val);
 }
 
-fifo8_push(>fifo, val);
+esp_update_drq(s);
 }
 
 static void esp_fifo_push_buf(ESPState *s, uint8_t *buf, int len)
@@ -184,11 +185,16 @@ static void esp_fifo_push_buf(ESPState *s, uint8_t *buf, 
int len)
 
 static uint8_t esp_fifo_pop(ESPState *s)
 {
+uint8_t val;
+
 if (fifo8_is_empty(>fifo)) {
-return 0;
+val = 0;
+} else {
+val = fifo8_pop(>fifo);
 }
 
-return fifo8_pop(>fifo);
+esp_update_drq(s);
+return val;
 }
 
 static uint32_t esp_fifo_pop_buf(ESPState *s, uint8_t *dest, int maxlen)
-- 
2.39.2




[PATCH 10/13] esp.c: introduce esp_update_drq() and update esp_fifo_{push, pop}_buf() to use it

2024-02-16 Thread Mark Cave-Ayland
This new function sets the DRQ line correctly according to the current transfer
mode, direction and FIFO contents. Update esp_fifo_push_buf() and 
esp_fifo_pop_buf()
to use it so that DRQ is always set correctly when reading/writing multiple 
bytes
to/from the FIFO.

Signed-off-by: Mark Cave-Ayland 
---
 hw/scsi/esp.c | 45 +
 1 file changed, 45 insertions(+)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 96aa576601..ca0fa5098d 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -124,6 +124,48 @@ void esp_request_cancelled(SCSIRequest *req)
 }
 }
 
+static void esp_update_drq(ESPState *s)
+{
+bool to_device;
+
+switch (esp_get_phase(s)) {
+case STAT_MO:
+case STAT_CD:
+case STAT_DO:
+to_device = true;
+break;
+
+case STAT_DI:
+case STAT_ST:
+case STAT_MI:
+to_device = false;
+break;
+
+default:
+return;
+}
+
+if (s->dma) {
+/* DMA request so update DRQ according to transfer direction */
+if (to_device) {
+if (fifo8_num_free(>fifo) < 2) {
+esp_lower_drq(s);
+} else {
+esp_raise_drq(s);
+}
+} else {
+if (fifo8_num_used(>fifo) < 2) {
+esp_lower_drq(s);
+} else {
+esp_raise_drq(s);
+}
+}
+} else {
+/* Not a DMA request */
+esp_lower_drq(s);
+}
+}
+
 static void esp_fifo_push(ESPState *s, uint8_t val)
 {
 if (fifo8_num_used(>fifo) == s->fifo.capacity) {
@@ -137,6 +179,7 @@ static void esp_fifo_push(ESPState *s, uint8_t val)
 static void esp_fifo_push_buf(ESPState *s, uint8_t *buf, int len)
 {
 fifo8_push_all(>fifo, buf, len);
+esp_update_drq(s);
 }
 
 static uint8_t esp_fifo_pop(ESPState *s)
@@ -155,6 +198,7 @@ static uint32_t esp_fifo_pop_buf(ESPState *s, uint8_t 
*dest, int maxlen)
 int len;
 
 if (maxlen == 0) {
+esp_update_drq(s);
 return 0;
 }
 
@@ -175,6 +219,7 @@ static uint32_t esp_fifo_pop_buf(ESPState *s, uint8_t 
*dest, int maxlen)
 n += n2;
 }
 
+esp_update_drq(s);
 return n;
 }
 
-- 
2.39.2




Re: [PATCH v4 00/10] Optimize buffer_is_zero

2024-02-16 Thread Alexander Monakov


On Thu, 15 Feb 2024, Richard Henderson wrote:

> On 2/15/24 13:37, Alexander Monakov wrote:
> > Ah, I guess you might be running at low perf_event_paranoid setting that
> > allows unprivileged sampling of kernel events? In our submissions the
> > percentage was for perf_event_paranoid=2, i.e. relative to Qemu only,
> > excluding kernel time under syscalls.
> 
> Ok.  Eliminating kernel samples makes things easier to see.
> But I still do not see a 40% reduction in runtime.

I suspect Mikhail's image was less sparse, so the impact from inlining
was greater.

> With this, I see virtually all of the runtime in libz.so.
> Therefore I converted this to raw first, to focus on the issue.

Ah, apologies for that. I built with --disable-default-features and
did not notice my qemu-img lacked support for vmdk and treated it
as a raw image instead. I was assuming it was similar to what Mikhail
used, but obviously it's not due to the compression.

> For avoidance of doubt:
> 
> $ ls -lsh test.raw && sha256sum test.raw
>  12G -rw-r--r--  1 rth  rth   40G Feb 15 21:14 test.raw
> 3b056d839952538fed42fa898c6063646f4fda1bf7ea0180fbb5f29d21fe8e80  test.raw
> 
> Host: 11th Gen Intel(R) Core(TM) i7-1195G7 @ 2.90GHz
> Compiler: gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
> 
> master:
>   57.48%  qemu-img-m  [.] buffer_zero_avx2
>3.60%  qemu-img-m  [.] is_allocated_sectors.part.0
>2.61%  qemu-img-m  [.] buffer_is_zero
>   63.69%  -- total
> 
> v3:
>   48.86%  qemu-img-v3  [.] is_allocated_sectors.part.0
>   3.79%  qemu-img-v3  [.] buffer_zero_avx2
>   52.65%  -- total
> -17%  -- reduction from master
> 
> v4:
>   54.60%  qemu-img-v4  [.] buffer_is_zero_ge256
>3.30%  qemu-img-v4  [.] buffer_zero_avx2
>3.17%  qemu-img-v4  [.] is_allocated_sectors.part.0
>   61.07%  -- total
>  -4%  -- reduction from master
> 
> v4+:
>   46.65%  qemu-img  [.] is_allocated_sectors.part.0
>3.49%  qemu-img  [.] buffer_zero_avx2
>0.05%  qemu-img  [.] buffer_is_zero_ge256
>   50.19%  -- total
> -21%  -- reduction from master

Any ideas where the -21% vs v3's -17% difference comes from?

FWIW, in situations like these I always recommend to run perf with fixed
sampling rate, i.e. 'perf record -e cycles:P -c 10' or 'perf record -e
cycles/period=10/P' to make sample counts between runs of different
duration directly comparable (displayed with 'perf report -n').

> The v4+ puts the 3 byte test back inline, like in your v3.
> 
> Importantly, it must be as 3 short-circuting tests, where my v4 "simplified"
> this to (s | m | e) != 0, on the assumption that the reduced number of
> branches would help.

Yes, we also noticed that when preparing our patch. We also tried mixed
variants like (s | e) != 0 || m != 0, but they did not turn out faster.

> With that settled, I guess we need to talk about how much the out-of-line
> implementation matters at all.  I'm thinking about writing a
> test/bench/bufferiszero, with all-zero buffers of various sizes and
> alignments.  With that it would be easier to talk about whether any given
> implementation is is an improvement for that final 4% not eliminated by the
> three bytes.

Yeah, initially I suggested this task to Mikhail as a practice exercise
outside of Qemu, and we had a benchmark that measures buffer_is_zero via
perf_event_open. This allows to see exactly how close the implementation
runs to the performance ceiling given by max L1 fetch rate (two loads
per cycle on x86).

Alexander



[PATCH] qapi: Misc cleanups to migrate QAPIs

2024-02-16 Thread Het Gala
Signed-off-by: Het Gala 
---
 qapi/migration.json | 13 +++--
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/qapi/migration.json b/qapi/migration.json
index 5a565d9b8d..5756e650b0 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -1728,6 +1728,7 @@
 #
 # -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } }
 # <- { "return": {} }
+#
 # -> { "execute": "migrate",
 #  "arguments": {
 #  "channels": [ { "channel-type": "main",
@@ -1796,19 +1797,19 @@
 #
 # 3. The uri format is the same as for -incoming
 #
-# 5. For now, number of migration streams is restricted to one,
+# 4. For now, number of migration streams is restricted to one,
 #i.e number of items in 'channels' list is just 1.
 #
-# 4. The 'uri' and 'channels' arguments are mutually exclusive;
+# 5. The 'uri' and 'channels' arguments are mutually exclusive;
 #exactly one of the two should be present.
 #
 # Example:
 #
 # -> { "execute": "migrate-incoming",
-#  "arguments": { "uri": "tcp::4446" } }
+#  "arguments": { "uri": "tcp:0:4446" } }
 # <- { "return": {} }
 #
-# -> { "execute": "migrate",
+# -> { "execute": "migrate-incoming",
 #  "arguments": {
 #  "channels": [ { "channel-type": "main",
 #  "addr": { "transport": "socket",
@@ -1817,7 +1818,7 @@
 #"port": "1050" } } ] } }
 # <- { "return": {} }
 #
-# -> { "execute": "migrate",
+# -> { "execute": "migrate-incoming",
 #  "arguments": {
 #  "channels": [ { "channel-type": "main",
 #  "addr": { "transport": "exec",
@@ -1825,7 +1826,7 @@
 #  "/some/sock" ] } } ] } }
 # <- { "return": {} }
 #
-# -> { "execute": "migrate",
+# -> { "execute": "migrate-incoming",
 #  "arguments": {
 #  "channels": [ { "channel-type": "main",
 #  "addr": { "transport": "rdma",
-- 
2.22.3




Re: [PATCH 1/6] hw/arm: Inline sysbus_create_simple(PL110 / PL111)

2024-02-16 Thread Philippe Mathieu-Daudé

On 16/2/24 18:14, BALATON Zoltan wrote:

On Fri, 16 Feb 2024, Philippe Mathieu-Daudé wrote:

We want to set another qdev property (a link) for the pl110
and pl111 devices, we can not use sysbus_create_simple() which
only passes sysbus base address and IRQs as arguments. Inline
it so we can set the link property in the next commit.

Signed-off-by: Philippe Mathieu-Daudé 
---
hw/arm/realview.c    |  5 -
hw/arm/versatilepb.c |  6 +-
hw/arm/vexpress.c    | 10 --
3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index 9058f5b414..77300e92e5 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -238,7 +238,10 @@ static void realview_init(MachineState *machine,
    sysbus_create_simple("pl061", 0x10014000, pic[7]);
    gpio2 = sysbus_create_simple("pl061", 0x10015000, pic[8]);

-    sysbus_create_simple("pl111", 0x1002, pic[23]);
+    dev = qdev_new("pl111");
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), _fatal);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x1002);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[23]);


Not directly related to this patch but this blows up 1 line into 4 just 
to allow setting a property. Maybe just to keep some simplicity we'd 
rather need either a sysbus_realize_simple function that takes a sysbus 
device instead of the name and does not create the device itself or some 
way to pass properties to sysbus create simple (but the latter may not 
be easy to do in a generic way so not sure about that). What do you think?


Unfortunately sysbus doesn't scale in heterogeneous setup.



[PATCH v2 0/2] Misc: Make watchdog devices using qemu_system_reset_request() use watchdog_perfom_action()

2024-02-16 Thread Abhiram Tilak
A few watchdog devices use qemu_system_reset_request(). This is not ideal since
behaviour of watchdog-expiry can't be changed by QMP using `watchdog_action`.
As stated in BiteSizedTasks wiki page, instead of using 
qemu_system_reset_request()
to reset when a watchdog timer expires, let watchdog_perform_action() decide
what to do.

v2:
 - Remove redundant comment in patch1 in m48t59.
 - Exclude patch 3 from patch series due to current call being more preferable.

Abhiram Tilak (2):
  misc: m48t59: replace qemu_system_reset_request() call with
watchdog_perform_action()
  misc: pxa2xx_timer: replace qemu_system_reset_request() call with
watchdog_perform_action()

 hw/rtc/m48t59.c | 4 ++--
 hw/timer/pxa2xx_timer.c | 3 ++-
 2 files changed, 4 insertions(+), 3 deletions(-)

-- 
2.42.1




[PATCH v2 2/2] misc: pxa2xx_timer: replace qemu_system_reset_request() call with watchdog_perform_action()

2024-02-16 Thread Abhiram Tilak
A few watchdog devices use qemu_system_reset_request(). This is not ideal since
behaviour of watchdog-expiry can't be changed by QMP using `watchdog_action`.
As stated in BiteSizedTasks wiki page, instead of using 
qemu_system_reset_request()
to reset when a watchdog timer expires, let watchdog_perform_action() decide
what to do.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2124
Signed-off-by: Abhiram Tilak 
---
 hw/timer/pxa2xx_timer.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c
index 6a7d5551f4..6479ab1a8b 100644
--- a/hw/timer/pxa2xx_timer.c
+++ b/hw/timer/pxa2xx_timer.c
@@ -18,6 +18,7 @@
 #include "qemu/log.h"
 #include "qemu/module.h"
 #include "qom/object.h"
+#include "sysemu/watchdog.h"
 
 #define OSMR0  0x00
 #define OSMR1  0x04
@@ -417,7 +418,7 @@ static void pxa2xx_timer_tick(void *opaque)
 if (t->num == 3)
 if (i->reset3 & 1) {
 i->reset3 = 0;
-qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+watchdog_perform_action();
 }
 }
 
-- 
2.42.1




[PATCH v2 1/2] misc: m48t59: replace qemu_system_reset_request() call with watchdog_perform_action()

2024-02-16 Thread Abhiram Tilak
A few watchdog devices use qemu_system_reset_request(). This is not ideal since
behaviour of watchdog-expiry can't be changed by QMP using `watchdog_action`.
As stated in BiteSizedTasks wiki page, instead of using 
qemu_system_reset_request()
to reset when a watchdog timer expires, let watchdog_perform_action() decide
what to do.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2124
Signed-off-by: Abhiram Tilak 
---
 hw/rtc/m48t59.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c
index aa44c4b20c..1585a2d399 100644
--- a/hw/rtc/m48t59.c
+++ b/hw/rtc/m48t59.c
@@ -36,6 +36,7 @@
 #include "qemu/bcd.h"
 #include "qemu/module.h"
 #include "trace.h"
+#include "sysemu/watchdog.h"
 
 #include "m48t59-internal.h"
 #include "migration/vmstate.h"
@@ -163,8 +164,7 @@ static void watchdog_cb (void *opaque)
 if (NVRAM->buffer[0x1FF7] & 0x80) {
 NVRAM->buffer[0x1FF7] = 0x00;
 NVRAM->buffer[0x1FFC] &= ~0x40;
-/* May it be a hw CPU Reset instead ? */
-qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+watchdog_perform_action();
 } else {
 qemu_set_irq(NVRAM->IRQ, 1);
 qemu_set_irq(NVRAM->IRQ, 0);
-- 
2.42.1




Re: [PATCH v3 3/6] target/arm: Adjust and validate mtedesc sizem1

2024-02-16 Thread Richard Henderson

On 2/16/24 05:12, Michael Tokarev wrote:

07.02.2024 05:52, Richard Henderson :

When we added SVE_MTEDESC_SHIFT, we effectively limited the
maximum size of MTEDESC.  Adjust SIZEM1 to consume the remaining
bits (32 - 10 - 5 - 12 == 5).  Assert that the data to be stored
fits within the field (expecting 8 * 4 - 1 == 31, exact fit).

Cc: qemu-sta...@nongnu.org
Reviewed-by: Peter Maydell 
Signed-off-by: Richard Henderson 
---
  target/arm/internals.h | 2 +-
  target/arm/tcg/translate-sve.c | 7 ---
  2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/target/arm/internals.h b/target/arm/internals.h
index fc337fe40e..50bff44549 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -1278,7 +1278,7 @@ FIELD(MTEDESC, TBI,   4, 2)
  FIELD(MTEDESC, TCMA,  6, 2)
  FIELD(MTEDESC, WRITE, 8, 1)
  FIELD(MTEDESC, ALIGN, 9, 3)
-FIELD(MTEDESC, SIZEM1, 12, SIMD_DATA_BITS - 12)  /* size - 1 */
+FIELD(MTEDESC, SIZEM1, 12, SIMD_DATA_BITS - SVE_MTEDESC_SHIFT - 12)  /* size - 
1 */
  bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr);
  uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t 
ra);
diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c
index 7108938251..a88e523cba 100644
--- a/target/arm/tcg/translate-sve.c
+++ b/target/arm/tcg/translate-sve.c
@@ -4443,17 +4443,18 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 
addr,

  {
  unsigned vsz = vec_full_reg_size(s);
  TCGv_ptr t_pg;
+    uint32_t sizem1;
  int desc = 0;
  assert(mte_n >= 1 && mte_n <= 4);
+    sizem1 = (mte_n << dtype_msz(dtype)) - 1;
+    assert(sizem1 <= R_MTEDESC_SIZEM1_MASK >> R_MTEDESC_SIZEM1_SHIFT);
  if (s->mte_active[0]) {
-    int msz = dtype_msz(dtype);
-
  desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s));
  desc = FIELD_DP32(desc, MTEDESC, TBI, s->tbid);
  desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma);
  desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write);
-    desc = FIELD_DP32(desc, MTEDESC, SIZEM1, (mte_n << msz) - 1);
+    desc = FIELD_DP32(desc, MTEDESC, SIZEM1, sizem1);
  desc <<= SVE_MTEDESC_SHIFT;
  } else {
  addr = clean_data_tbi(s, addr);


There's no question about stable-8.2 here, this change needed there.
But I've a question about stable-7.2 - does it make sense to pick this
one up for 7.2?  It quickly goes out of control, because this one
is on top of

  523da6b963455ce0a0e8d572d98d9cd91f952785 target/arm: Check alignment in 
helper_mte_check
  (this one might be good for 7.2 by its own)
  which needs:
   3b97520c86e704b0533627c26b98173b71834bad target/arm: Pass single_memop to 
gen_mte_checkN
   which needs:
    6f47e7c18972802c428a5e03eb52a8f0a7bebe5c target/arm: Load/store integer pair with one 
tcg operation

    which needs:
     needs 128bit ops
     659aed5feda4472d8aed4ccc69e125bba2af8b89 target/arm: Drop tcg_temp_free from 
translator-a64.c

     ...

So I think it's not a good idea to go down this hole..

Probably ditto for the other two:
   target/arm: Split out make_svemte_desc
   target/arm: Handle mte in do_ldrq, do_ldro

Makes sense?  Or it's better to do a proper backport?


I don't think it makes sense to go back too far.

r~




Re: [PATCH v2 1/3] trans_rvv.c.inc: write CSRs must call mark_vs_dirty() too

2024-02-16 Thread Richard Henderson

On 2/16/24 03:57, Daniel Henrique Barboza wrote:

In the Vector spec section 3.2 [1]:

"When mstatus.VS is set to Initial or Clean, executing any instruction
  that changes vector state, including the vector CSRs, will change
  mstatus.VS to Dirty."

ldst_us_trans(), ldst_stride_trans(), ldst_index_trans() and
ldst_whole_trans() will change vector state regardless of being a store
op or not. Stores will set env->vstart to zero after execution (see
vext_ldst_us() in vector_helper.c), and this is vector CSR state change.


In Initial or Clean state, it could be argued that vstart is already zero, so is there 
really a change to state?


OTOH, on the exception path out of a vector store, where we *do* set vstart != 0, we do 
not also set vs dirty.


Therefore I think that loads and stores need to manage dirty within the helper, alongside 
the management of vstart, or perhaps move the mark_vs_dirty call to *before* the call to 
the helper.



r~



Re: [PATCH v2 3/3] target/riscv/translate.c: set vstart_eq_zero in mark_vs_dirty()

2024-02-16 Thread Richard Henderson

On 2/16/24 03:57, Daniel Henrique Barboza wrote:

The 'vstart_eq_zero' flag which is used to determine if some insns, like
vector reductor operations, should SIGILL. At this moment the flag is
being updated only during cpu_get_tb_cpu_state(), at the start of each
translation block.

This cadence isn't enough and we're facing situations where a vector
instruction successfully updated 'vstart' to zero, but the flag was
still marked as 'false', resulting in a SIGILL because instructions are
checking the flag.

mark_vs_dirty() is called after any instruction changes Vector CSR
state, making it a good place to update 'vstart_eq_zero'.

Fixes: 8e1ee1fb57 ("target/riscv: rvv-1.0: add translation-time vector context 
status")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1976
Signed-off-by: Daniel Henrique Barboza 
---
  target/riscv/translate.c | 20 
  1 file changed, 20 insertions(+)

diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 177418b2b9..f9ff7b6173 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -652,6 +652,8 @@ static inline void mark_fs_dirty(DisasContext *ctx) { }
   */
  static void mark_vs_dirty(DisasContext *ctx)
  {
+TCGLabel *vstart_zero = gen_new_label();
+TCGLabel *done = gen_new_label();
  TCGv tmp;
  
  if (ctx->mstatus_vs != EXT_STATUS_DIRTY) {

@@ -669,6 +671,24 @@ static void mark_vs_dirty(DisasContext *ctx)
  tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs));
  }
  }
+
+/*
+ * We can safely make 'vl_eq_vlmax = false' if we marked
+ * VS as dirty with non-zero 'vstart', i.e. there's a fault
+ * to be handled. If 'vstart' is zero then we should retain
+ * the existing 'vl_eq_vlmax' - it'll be recalculated on the
+ * start of the next TB or during vset{i}vl{i} (that forces a
+ * TB end).
+ */
+tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vstart, 0, vstart_zero);
+ctx->vstart_eq_zero = false;
+ctx->vl_eq_vlmax = false;
+tcg_gen_br(done);
+
+gen_set_label(vstart_zero);
+ctx->vstart_eq_zero = true;
+
+gen_set_label(done);


This is very confused, apparently generating code to test vstart at runtime, and then set 
some translation time variables in the branches.


Afaik, the only way vstart != 0 is an explicit set to the CSR or exiting a load via 
exception.  Therefore you have no need to have any sort of brcond here -- just set

ctx->vstart_eq_zero = true.

Also, you need to move the ifdefs from around mark_vs_dirty, because it is now relevant to 
user-only.


It may be worth a rename, because it does more than mark vs dirty, and therefore is 
different than mark_fs_dirty.


You need to review all instances of

TCGLabel *over = gen_new_label();
tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
...
gen_set_label(over);
return true;

because this *should have* set vstart = 0.  With vstart < vl, this is done in the helper 
function, but every place using a conditional branch needs attention.



r~



Re: [PATCH v3 3/3] hw/cxl/cxl-mailbox-utils: Add device DDR5 ECS control feature

2024-02-16 Thread fan
On Thu, Feb 15, 2024 at 07:01:46PM +0800, shiju.j...@huawei.com wrote:
> 56.china.huawei.com (7.191.161.198)
> Status: O
> Content-Length: 7949
> Lines: 181
> 
> From: Shiju Jose 
> 
> CXL spec 3.1 section 8.2.9.9.11.2 describes the DDR5 Error Check Scrub (ECS)
> control feature.
> 
> The Error Check Scrub (ECS) is a feature defined in JEDEC DDR5 SDRAM
> Specification (JESD79-5) and allows the DRAM to internally read, correct
> single-bit errors, and write back corrected data bits to the DRAM array
> while providing transparency to error counts. The ECS control feature
> allows the request to configure ECS input configurations during system
> boot or at run-time.
> 
> The ECS control allows the requester to change the log entry type, the ECS
> threshold count provided that the request is within the definition
> specified in DDR5 mode registers, change mode between codeword mode and
> row count mode, and reset the ECS counter.
> 
> Reviewed-by: Davidlohr Bueso 
> Signed-off-by: Shiju Jose 

Reviewed-by: Fan Ni 

> ---
>  hw/cxl/cxl-mailbox-utils.c | 100 -
>  1 file changed, 99 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c
> index 9557c38dd9..a14eee9ff5 100644
> --- a/hw/cxl/cxl-mailbox-utils.c
> +++ b/hw/cxl/cxl-mailbox-utils.c
> @@ -998,6 +998,7 @@ typedef struct CXLSupportedFeatureEntry {
>  
>  enum CXL_SUPPORTED_FEATURES_LIST {
>  CXL_FEATURE_PATROL_SCRUB = 0,
> +CXL_FEATURE_DDR5_ECS,
>  CXL_FEATURE_MAX
>  };
>  
> @@ -1069,6 +1070,42 @@ typedef struct CXLMemPatrolScrubSetFeature {
>  CXLMemPatrolScrubWriteAttrbs feat_data;
>  } QEMU_PACKED QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature;
>  
> +/*
> + * CXL r3.1 section 8.2.9.9.11.2:
> + * DDR5 Error Check Scrub (ECS) Control Feature
> + */
> +static const QemuUUID ddr5_ecs_uuid = {
> +.data = UUID(0xe5b13f22, 0x2328, 0x4a14, 0xb8, 0xba,
> + 0xb9, 0x69, 0x1e, 0x89, 0x33, 0x86)
> +};
> +
> +#define CXL_DDR5_ECS_GET_FEATURE_VERSION0x01
> +#define CXL_DDR5_ECS_SET_FEATURE_VERSION0x01
> +#define CXL_DDR5_ECS_LOG_ENTRY_TYPE_DEFAULT0x01
> +#define CXL_DDR5_ECS_REALTIME_REPORT_CAP_DEFAULT1
> +#define CXL_DDR5_ECS_THRESHOLD_COUNT_DEFAULT3 /* 3: 256, 4: 1024, 5: 
> 4096 */
> +#define CXL_DDR5_ECS_MODE_DEFAULT0
> +
> +#define CXL_DDR5_ECS_NUM_MEDIA_FRUS   3
> +
> +/* CXL memdev DDR5 ECS control attributes */
> +struct CXLMemECSReadAttrbs {
> +uint8_t ecs_log_cap;
> +uint8_t ecs_cap;
> +uint16_t ecs_config;
> +uint8_t ecs_flags;
> +} QEMU_PACKED cxl_ddr5_ecs_feat_read_attrbs[CXL_DDR5_ECS_NUM_MEDIA_FRUS];
> +
> +typedef struct CXLDDR5ECSWriteAttrbs {
> +uint8_t ecs_log_cap;
> +uint16_t ecs_config;
> +} QEMU_PACKED CXLDDR5ECSWriteAttrbs;
> +
> +typedef struct CXLDDR5ECSSetFeature {
> +CXLSetFeatureInHeader hdr;
> +CXLDDR5ECSWriteAttrbs feat_data[];
> +} QEMU_PACKED QEMU_ALIGNED(16) CXLDDR5ECSSetFeature;
> +
>  /* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */
>  static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
>   uint8_t *payload_in,
> @@ -1087,7 +1124,7 @@ static CXLRetCode cmd_features_get_supported(const 
> struct cxl_cmd *cmd,
>  CXLSupportedFeatureHeader hdr;
>  CXLSupportedFeatureEntry feat_entries[];
>  } QEMU_PACKED QEMU_ALIGNED(16) * get_feats_out = (void *)payload_out;
> -uint16_t index;
> +uint16_t count, index;
>  uint16_t entry, req_entries;
>  uint16_t feat_entries = 0;
>  
> @@ -1129,6 +1166,35 @@ static CXLRetCode cmd_features_get_supported(const 
> struct cxl_cmd *cmd,
>  cxl_memdev_ps_feat_read_attrbs.scrub_flags =
>  CXL_MEMDEV_PS_ENABLE_DEFAULT;
>  break;
> +case  CXL_FEATURE_DDR5_ECS:
> +/* Fill supported feature entry for device DDR5 ECS control */
> +get_feats_out->feat_entries[entry] =
> + (struct CXLSupportedFeatureEntry) {
> +.uuid = ddr5_ecs_uuid,
> +.feat_index = index,
> +.get_feat_size = CXL_DDR5_ECS_NUM_MEDIA_FRUS *
> +sizeof(struct CXLMemECSReadAttrbs),
> +.set_feat_size = CXL_DDR5_ECS_NUM_MEDIA_FRUS *
> +sizeof(CXLDDR5ECSWriteAttrbs),
> +.attrb_flags = 0x1,
> +.get_feat_version = CXL_DDR5_ECS_GET_FEATURE_VERSION,
> +.set_feat_version = CXL_DDR5_ECS_SET_FEATURE_VERSION,
> +.set_feat_effects = 0,
> +};
> +feat_entries++;
> +/* Set default value for DDR5 ECS read attributes */
> +for (count = 0; count < CXL_DDR5_ECS_NUM_MEDIA_FRUS; count++) {
> +cxl_ddr5_ecs_feat_read_attrbs[count].ecs_log_cap =
> +   

Re: [PATCH 1/3] misc: m48t59: replace qemu_system_reset_request() call with watchdog_perform_action()

2024-02-16 Thread atp exp
I agree, comment here is redundant, i will fix
it in the next patch.

Abhiram

On Fri, 16 Feb 2024 at 20:19, Peter Maydell 
wrote:

> On Fri, 16 Feb 2024 at 13:56, Abhiram Tilak  wrote:
> >
> > A few watchdog devices use qemu_system_reset_request(). This is not
> ideal since
> > behaviour of watchdog-expiry can't be changed by QMP using
> `watchdog_action`.
> > As stated in BiteSizedTasks wiki page, instead of using
> qemu_system_reset_request()
> > to reset when a watchdog timer expires, let watchdog_perform_action()
> decide
> > what to do.
> >
> > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2124
> > Signed-off-by: Abhiram Tilak 
> > ---
> >  hw/rtc/m48t59.c | 4 ++--
> >  1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c
> > index aa44c4b20c..ebda084478 100644
> > --- a/hw/rtc/m48t59.c
> > +++ b/hw/rtc/m48t59.c
> > @@ -36,6 +36,7 @@
> >  #include "qemu/bcd.h"
> >  #include "qemu/module.h"
> >  #include "trace.h"
> > +#include "sysemu/watchdog.h"
> >
> >  #include "m48t59-internal.h"
> >  #include "migration/vmstate.h"
> > @@ -163,8 +164,7 @@ static void watchdog_cb (void *opaque)
> >  if (NVRAM->buffer[0x1FF7] & 0x80) {
> >  NVRAM->buffer[0x1FF7] = 0x00;
> >  NVRAM->buffer[0x1FFC] &= ~0x40;
> > -/* May it be a hw CPU Reset instead ? */
> > -qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
> > +watchdog_perform_action(); /* watchdog-expired action */
> >  } else {
>
> I don't think we need the comment, it's clear enough from the
> name of the function.
>
> Otherwise
> Reviewed-by: Peter Maydell 
>
> thanks
> -- PMM
>


Re: [PATCH v3 2/3] hw/cxl/cxl-mailbox-utils: Add device patrol scrub control feature

2024-02-16 Thread fan
On Thu, Feb 15, 2024 at 07:01:45PM +0800, shiju.j...@huawei.com wrote:
> From: Shiju Jose 
> 
> CXL spec 3.1 section 8.2.9.9.11.1 describes the device patrol scrub control
> feature. The device patrol scrub proactively locates and makes corrections
> to errors in regular cycle. The patrol scrub control allows the request to
> configure patrol scrub input configurations.
> 
> The patrol scrub control allows the requester to specify the number of
> hours for which the patrol scrub cycles must be completed, provided that
> the requested number is not less than the minimum number of hours for the
> patrol scrub cycle that the device is capable of. In addition, the patrol
> scrub controls allow the host to disable and enable the feature in case
> disabling of the feature is needed for other purposes such as
> performance-aware operations which require the background operations to be
> turned off.
> 
> Reviewed-by: Davidlohr Bueso 
> Signed-off-by: Shiju Jose 
> ---

Reviewed-by: Fan Ni 

>  hw/cxl/cxl-mailbox-utils.c | 97 +-
>  1 file changed, 96 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c
> index f761ac49b5..9557c38dd9 100644
> --- a/hw/cxl/cxl-mailbox-utils.c
> +++ b/hw/cxl/cxl-mailbox-utils.c
> @@ -997,6 +997,7 @@ typedef struct CXLSupportedFeatureEntry {
>  } QEMU_PACKED CXLSupportedFeatureEntry;
>  
>  enum CXL_SUPPORTED_FEATURES_LIST {
> +CXL_FEATURE_PATROL_SCRUB = 0,
>  CXL_FEATURE_MAX
>  };
>  
> @@ -1037,6 +1038,37 @@ enum CXL_SET_FEATURE_FLAG_DATA_TRANSFER {
>  CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MAX
>  };
>  
> +/* CXL r3.1 section 8.2.9.9.11.1: Device Patrol Scrub Control Feature */
> +static const QemuUUID patrol_scrub_uuid = {
> +.data = UUID(0x96dad7d6, 0xfde8, 0x482b, 0xa7, 0x33,
> + 0x75, 0x77, 0x4e, 0x06, 0xdb, 0x8a)
> +};
> +
> +#define CXL_MEMDEV_PS_GET_FEATURE_VERSION0x01
> +#define CXL_MEMDEV_PS_SET_FEATURE_VERSION0x01
> +#define CXL_MEMDEV_PS_SCRUB_CYCLE_CHANGE_CAP_DEFAULTBIT(0)
> +#define CXL_MEMDEV_PS_SCRUB_REALTIME_REPORT_CAP_DEFAULTBIT(1)
> +#define CXL_MEMDEV_PS_CUR_SCRUB_CYCLE_DEFAULT12
> +#define CXL_MEMDEV_PS_MIN_SCRUB_CYCLE_DEFAULT1
> +#define CXL_MEMDEV_PS_ENABLE_DEFAULT0
> +
> +/* CXL memdev patrol scrub control attributes */
> +struct CXLMemPatrolScrubReadAttrbs {
> +uint8_t scrub_cycle_cap;
> +uint16_t scrub_cycle;
> +uint8_t scrub_flags;
> +} QEMU_PACKED cxl_memdev_ps_feat_read_attrbs;
> +
> +typedef struct CXLMemPatrolScrubWriteAttrbs {
> +uint8_t scrub_cycle_hr;
> +uint8_t scrub_flags;
> +} QEMU_PACKED CXLMemPatrolScrubWriteAttrbs;
> +
> +typedef struct CXLMemPatrolScrubSetFeature {
> +CXLSetFeatureInHeader hdr;
> +CXLMemPatrolScrubWriteAttrbs feat_data;
> +} QEMU_PACKED QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature;
> +
>  /* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */
>  static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
>   uint8_t *payload_in,
> @@ -1060,7 +1092,7 @@ static CXLRetCode cmd_features_get_supported(const 
> struct cxl_cmd *cmd,
>  uint16_t feat_entries = 0;
>  
>  if (get_feats_in->count < sizeof(CXLSupportedFeatureHeader) ||
> -get_feats_in->start_index > CXL_FEATURE_MAX) {
> +get_feats_in->start_index >= CXL_FEATURE_MAX) {
>  return CXL_MBOX_INVALID_INPUT;
>  }
>  req_entries = (get_feats_in->count -
> @@ -1072,6 +1104,31 @@ static CXLRetCode cmd_features_get_supported(const 
> struct cxl_cmd *cmd,
>  entry = 0;
>  while (entry < req_entries) {
>  switch (index) {
> +case  CXL_FEATURE_PATROL_SCRUB:
> +/* Fill supported feature entry for device patrol scrub control 
> */
> +get_feats_out->feat_entries[entry] =
> +   (struct CXLSupportedFeatureEntry) {
> +.uuid = patrol_scrub_uuid,
> +.feat_index = index,
> +.get_feat_size = sizeof(cxl_memdev_ps_feat_read_attrbs),
> +.set_feat_size = sizeof(CXLMemPatrolScrubWriteAttrbs),
> +/* Bit[0] : 1, feature attributes changeable */
> +.attrb_flags = 0x1,
> +.get_feat_version = CXL_MEMDEV_PS_GET_FEATURE_VERSION,
> +.set_feat_version = CXL_MEMDEV_PS_SET_FEATURE_VERSION,
> +.set_feat_effects = 0,
> +};
> +feat_entries++;
> +/* Set default value for device patrol scrub read attributes */
> +cxl_memdev_ps_feat_read_attrbs.scrub_cycle_cap =
> +CXL_MEMDEV_PS_SCRUB_CYCLE_CHANGE_CAP_DEFAULT 
> |
> +
> CXL_MEMDEV_PS_SCRUB_REALTIME_REPORT_CAP_DEFAULT;
> +cxl_memdev_ps_feat_read_attrbs.scrub_cycle =
> +

Re: [PATCH v3 1/3] hw/cxl/cxl-mailbox-utils: Add support for feature commands (8.2.9.6)

2024-02-16 Thread fan
On Thu, Feb 15, 2024 at 07:01:44PM +0800, shiju.j...@huawei.com wrote:
> From: Shiju Jose 
> 
> CXL spec 3.1 section 8.2.9.6 describes optional device specific features.
> CXL devices supports features with changeable attributes.
> Get Supported Features retrieves the list of supported device specific
> features. The settings of a feature can be retrieved using Get Feature and
> optionally modified using Set Feature.
> 
> Reviewed-by: Davidlohr Bueso 
> Signed-off-by: Shiju Jose 

Reviewed-by: Fan Ni 

> ---
>  hw/cxl/cxl-mailbox-utils.c | 175 +
>  1 file changed, 175 insertions(+)
> 
> diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c
> index 80a80f1ec2..f761ac49b5 100644
> --- a/hw/cxl/cxl-mailbox-utils.c
> +++ b/hw/cxl/cxl-mailbox-utils.c
> @@ -66,6 +66,10 @@ enum {
>  LOGS= 0x04,
>  #define GET_SUPPORTED 0x0
>  #define GET_LOG   0x1
> +FEATURES= 0x05,
> +#define GET_SUPPORTED 0x0
> +#define GET_FEATURE   0x1
> +#define SET_FEATURE   0x2
>  IDENTIFY= 0x40,
>  #define MEMORY_DEVICE 0x0
>  CCLS= 0x41,
> @@ -965,6 +969,165 @@ static CXLRetCode cmd_logs_get_log(const struct cxl_cmd 
> *cmd,
>  return CXL_MBOX_SUCCESS;
>  }
>  
> +/* CXL r3.1 section 8.2.9.6: Features */
> +/*
> + * Get Supported Features output payload
> + * CXL r3.1 section 8.2.9.6.1 Table 8-96
> + */
> +typedef struct CXLSupportedFeatureHeader {
> +uint16_t entries;
> +uint16_t nsuppfeats_dev;
> +uint32_t reserved;
> +} QEMU_PACKED CXLSupportedFeatureHeader;
> +
> +/*
> + * Get Supported Features Supported Feature Entry
> + * CXL r3.1 section 8.2.9.6.1 Table 8-97
> + */
> +typedef struct CXLSupportedFeatureEntry {
> +QemuUUID uuid;
> +uint16_t feat_index;
> +uint16_t get_feat_size;
> +uint16_t set_feat_size;
> +uint32_t attrb_flags;
> +uint8_t get_feat_version;
> +uint8_t set_feat_version;
> +uint16_t set_feat_effects;
> +uint8_t rsvd[18];
> +} QEMU_PACKED CXLSupportedFeatureEntry;
> +
> +enum CXL_SUPPORTED_FEATURES_LIST {
> +CXL_FEATURE_MAX
> +};
> +
> +/* Get Feature CXL 3.1 Spec 8.2.9.6.2 */
> +/*
> + * Get Feature input payload
> + * CXL r3.1 section 8.2.9.6.2 Table 8-99
> + */
> +/* Get Feature : Payload in selection */
> +enum CXL_GET_FEATURE_SELECTION {
> +CXL_GET_FEATURE_SEL_CURRENT_VALUE,
> +CXL_GET_FEATURE_SEL_DEFAULT_VALUE,
> +CXL_GET_FEATURE_SEL_SAVED_VALUE,
> +CXL_GET_FEATURE_SEL_MAX
> +};
> +
> +/* Set Feature CXL 3.1 Spec 8.2.9.6.3 */
> +/*
> + * Set Feature input payload
> + * CXL r3.1 section 8.2.9.6.3 Table 8-101
> + */
> +typedef struct CXLSetFeatureInHeader {
> +QemuUUID uuid;
> +uint32_t flags;
> +uint16_t offset;
> +uint8_t version;
> +uint8_t rsvd[9];
> +} QEMU_PACKED QEMU_ALIGNED(16) CXLSetFeatureInHeader;
> +
> +/* Set Feature : Payload in flags */
> +#define CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MASK   0x7
> +enum CXL_SET_FEATURE_FLAG_DATA_TRANSFER {
> +CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER,
> +CXL_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER,
> +CXL_SET_FEATURE_FLAG_CONTINUE_DATA_TRANSFER,
> +CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER,
> +CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER,
> +CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MAX
> +};
> +
> +/* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */
> +static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
> + uint8_t *payload_in,
> + size_t len_in,
> + uint8_t *payload_out,
> + size_t *len_out,
> + CXLCCI *cci)
> +{
> +struct {
> +uint32_t count;
> +uint16_t start_index;
> +uint16_t reserved;
> +} QEMU_PACKED QEMU_ALIGNED(16) * get_feats_in = (void *)payload_in;
> +
> +struct {
> +CXLSupportedFeatureHeader hdr;
> +CXLSupportedFeatureEntry feat_entries[];
> +} QEMU_PACKED QEMU_ALIGNED(16) * get_feats_out = (void *)payload_out;
> +uint16_t index;
> +uint16_t entry, req_entries;
> +uint16_t feat_entries = 0;
> +
> +if (get_feats_in->count < sizeof(CXLSupportedFeatureHeader) ||
> +get_feats_in->start_index > CXL_FEATURE_MAX) {
> +return CXL_MBOX_INVALID_INPUT;
> +}
> +req_entries = (get_feats_in->count -
> +   sizeof(CXLSupportedFeatureHeader)) /
> +   sizeof(CXLSupportedFeatureEntry);
> +req_entries = MIN(req_entries, CXL_FEATURE_MAX);
> +index = get_feats_in->start_index;
> +
> +entry = 0;
> +while (entry < req_entries) {
> +switch (index) {
> +default:
> +break;
> +}
> +index++;
> +entry++;
> +}
> +
> +get_feats_out->hdr.nsuppfeats_dev = 

[PATCH] configure: do not require gcc runtime library for firmwares

2024-02-16 Thread Marek Marczykowski-Górecki
probe_target_compiler() when checking for multilib support checks if
-nostdlib works together with -lgcc. It isn't necessary for building
various components in pc-bios/optionrom, as evidenced by looking at
actually used link flags there.
Alpine Linux for x86_64 does not ship with 32bit libgcc, but its gcc is
otherwise perfectly capable of building firmwares in pc-bios/optionrom
dir. Make configure recognize this situation.

Keep requiring functional -lgcc in other places.

Signed-off-by: Marek Marczykowski-Górecki 
---
 configure | 10 +++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/configure b/configure
index ff058d6c48..7721999f49 100755
--- a/configure
+++ b/configure
@@ -1216,7 +1216,7 @@ have_target() {
   return 1
 }
 
-# probe_target_compiler TARGET
+# probe_target_compiler TARGET [nostdlib-extra-ldflags]
 #
 # Look for a compiler for the given target, either native or cross.
 # Set variables target_* if a compiler is found, and container_cross_*
@@ -1226,6 +1226,9 @@ have_target() {
 #
 # If TARGET is a user-mode emulation target, also set build_static to
 # "y" if static linking is possible.
+# When testing -nostdlib build, -lgcc will be added for more extensive multilib
+# support test, but the -lgcc can be overriden with the second argument to the
+# function.
 #
 probe_target_compiler() {
   # reset all output variables
@@ -1243,6 +1246,7 @@ probe_target_compiler() {
   container_cross_strip=
 
   target_arch=${1%%-*}
+  nostdlib_ldflags=${2--lgcc}
   case $target_arch in
 aarch64) container_hosts="x86_64 aarch64" ;;
 alpha) container_hosts=x86_64 ;;
@@ -1432,7 +1436,7 @@ probe_target_compiler() {
 case $1 in
   *-softmmu)
 if do_compiler "$target_cc" $target_cflags -o $TMPO -c $TMPC &&
-  do_compiler "$target_cc" $target_cflags -r -nostdlib -o 
"${TMPDIR1}/${TMPB}2.o" "$TMPO" -lgcc; then
+  do_compiler "$target_cc" $target_cflags -r -nostdlib -o 
"${TMPDIR1}/${TMPB}2.o" "$TMPO" $nostdlib_ldflags; then
   got_cross_cc=yes
   break
 fi
@@ -1544,7 +1548,7 @@ echo "# Automatically generated by configure - do not 
modify" > Makefile.prereqs
 if have_target i386-softmmu x86_64-softmmu && \
 test "$host_os" != "darwin" && test "$host_os" != "sunos" && \
 test "$host_os" != "haiku" && \
-probe_target_compiler i386-softmmu; then
+probe_target_compiler i386-softmmu ""; then
 subdirs="$subdirs pc-bios/optionrom"
 config_mak=pc-bios/optionrom/config.mak
 echo "# Automatically generated by configure - do not modify" > $config_mak
-- 
2.43.0




Re: [PATCH 3/3] misc: ppc/spapr: replace qemu_system_reset_request() calls with watchdog_perform_action()

2024-02-16 Thread atp exp
I will exclude this patch from the series for now.
According to the discussions, the current code honours the
guest's preference.
Will wait for the enhancements needed in watchdog QAPI.

Abhiram

On Fri, 16 Feb 2024 at 20:24, Peter Maydell 
wrote:

> On Fri, 16 Feb 2024 at 13:56, Abhiram Tilak  wrote:
> >
> > A few watchdog devices use qemu_system_reset_request(). This is not
> ideal since
> > behaviour of watchdog-expiry can't be changed by QMP using
> `watchdog_action`.
> > As stated in BiteSizedTasks wiki page, instead of using
> qemu_system_reset_request()
> > to reset when a watchdog timer expires, let watchdog_perform_action()
> decide
> > what to do.
> >
> > I am unsure about the changes in `spapr_watchdog.c` in patch 3, it would
> be great
> > if any of the maintainers review it.
> >
> > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2124
> > Signed-off-by: Abhiram Tilak 
> > ---
> >  hw/watchdog/spapr_watchdog.c | 3 ++-
> >  1 file changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/hw/watchdog/spapr_watchdog.c b/hw/watchdog/spapr_watchdog.c
> > index 2bb1d3c532..9751b19506 100644
> > --- a/hw/watchdog/spapr_watchdog.c
> > +++ b/hw/watchdog/spapr_watchdog.c
> > @@ -18,6 +18,7 @@
> >  #include "target/ppc/cpu.h"
> >  #include "migration/vmstate.h"
> >  #include "trace.h"
> > +#include "sysemu/watchdog.h"
> >
> >  #include "hw/ppc/spapr.h"
> >
> > @@ -114,7 +115,7 @@ static void watchdog_expired(void *pw)
> >  qemu_system_vmstop_request(RUN_STATE_SHUTDOWN);
> >  break;
> >  case PSERIES_WDTF_ACTION_HARD_RESTART:
> > -qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
> > +watchdog_perform_action();
> >  break;
> >  case PSERIES_WDTF_ACTION_DUMP_RESTART:
> >  CPU_FOREACH(cs) {
>
> This one is more complicated, because the spapr watchdog
> has multiple possible behaviours which the guest can ask for.
>
> We had a discussion on the mailing list about this a little while back:
>
> https://lore.kernel.org/qemu-devel/CAFEAcA_KjSgt-oC=d2m6wadqorsucs1w_ji7ng2fgvjxawl...@mail.gmail.com/
>
> The conclusion was that the watchdog-behaviour QAPI API
> needs to be enhanced to be able to handle this kind of
> "the guest picks an action" watchdog, so that the user can
> either override the guest's choice, or request that the
> behaviour be what the guest wants it to be.
>
> thanks
> -- PMM
>


[RFC] Convert VMWARE vmdk (snapshot) to raw disk

2024-02-16 Thread Jinpu Wang
Hi,

We want to convert some VMWARE VM to KVM, and to reduce the VM down
time, we want to do it in two steps copy approach:

1. a snapshot is taken
- source VM continues to run on VMware => diff creates

2. snapshot is available for download as vmdk
- we need a software to copy snapshot to target VM raw disk

3. source VM is shut down
- diff is available

4. diff needs to be applied to target raw disk
is qemu-img able to do it, or is there another tool?  I saw commit
98eb9733f4cf2eeab6d12db7e758665d2fd5367b
Author: Sam Eiderman 
Date:   Thu Jun 20 12:10:57 2019 +0300

vmdk: Add read-only support for seSparse snapshots

So it seems qemu vmdk already support the seSparse snapshot format,
but it is unclear for us how to connect all these features together.

In short we want to
1 vmdk => raw (big size)
2 vmdk delta => same raw disk (later time, with less content)

Can you give us some suggestions?

Thx!
Jinpu Wang



Re: [PATCH v3 04/11] {linux,bsd}-user: Pass pid to fork_end()

2024-02-16 Thread Alex Bennée
Ilya Leoshkevich  writes:

> The upcoming follow-fork-mode child support requires knowing the child
> pid. Pass it down.
>
> Signed-off-by: Ilya Leoshkevich 
> ---
>  bsd-user/freebsd/os-proc.h  | 6 +++---
>  bsd-user/main.c | 4 +++-
>  bsd-user/qemu.h | 2 +-
>  linux-user/main.c   | 4 +++-
>  linux-user/syscall.c| 6 +++---
>  linux-user/user-internals.h | 2 +-
>  6 files changed, 14 insertions(+), 10 deletions(-)
>
> diff --git a/bsd-user/freebsd/os-proc.h b/bsd-user/freebsd/os-proc.h
> index d6418780344..3003c8cb637 100644
> --- a/bsd-user/freebsd/os-proc.h
> +++ b/bsd-user/freebsd/os-proc.h
> @@ -208,7 +208,7 @@ static inline abi_long do_freebsd_fork(void *cpu_env)
>   */
>  set_second_rval(cpu_env, child_flag);
>  
> -fork_end(child_flag);
> +fork_end(ret);
>  
>  return ret;
>  }
> @@ -252,7 +252,7 @@ static inline abi_long do_freebsd_rfork(void *cpu_env, 
> abi_long flags)
>   * value: 0 for parent process, 1 for child process.
>   */
>  set_second_rval(cpu_env, child_flag);
> -fork_end(child_flag);
> +fork_end(ret);
>  
>  return ret;
>  
> @@ -285,7 +285,7 @@ static inline abi_long do_freebsd_pdfork(void *cpu_env, 
> abi_ulong target_fdp,
>   * value: 0 for parent process, 1 for child process.
>   */
>  set_second_rval(cpu_env, child_flag);
> -fork_end(child_flag);
> +fork_end(ret);
>  
>  return ret;
>  }
> diff --git a/bsd-user/main.c b/bsd-user/main.c
> index bfe6888ea89..bc233a85cef 100644
> --- a/bsd-user/main.c
> +++ b/bsd-user/main.c
> @@ -109,8 +109,10 @@ void fork_start(void)
>  gdbserver_fork_start();
>  }
>  
> -void fork_end(int child)
> +void fork_end(abi_long pid)
>  {
> +int child = pid == 0;
> +
>  if (child) {
>  CPUState *cpu, *next_cpu;
>  /*
> diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h
> index dc842fffa7d..2414a87559b 100644
> --- a/bsd-user/qemu.h
> +++ b/bsd-user/qemu.h
> @@ -180,7 +180,7 @@ void cpu_loop(CPUArchState *env);
>  char *target_strerror(int err);
>  int get_osversion(void);
>  void fork_start(void);
> -void fork_end(int child);
> +void fork_end(abi_long pid);
>  
>  #include "qemu/log.h"
>  
> diff --git a/linux-user/main.c b/linux-user/main.c
> index 8c7bea1c631..f1a0267816b 100644
> --- a/linux-user/main.c
> +++ b/linux-user/main.c
> @@ -147,8 +147,10 @@ void fork_start(void)
>  gdbserver_fork_start();
>  }
>  
> -void fork_end(int child)
> +void fork_end(abi_long pid)
>  {
> +int child = pid == 0;
> +

The intent is clearer if we make it a bool:

  bool child = pid == 0;

Otherwise:

Reviewed-by: Alex Bennée 

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro



Re: [PATCH v3 03/11] gdbstub: Introduce gdbserver_fork_start()

2024-02-16 Thread Alex Bennée
Ilya Leoshkevich  writes:

> The upcoming follow-fork-mode child support requires knowing when
> fork() is about to happen in order to initialize its state. Add a hook
> for that.
>
> Signed-off-by: Ilya Leoshkevich 

Reviewed-by: Alex Bennée 

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro



Re: [PATCH v3 02/11] {linux,bsd}-user: Update ts_tid after fork()

2024-02-16 Thread Alex Bennée
Ilya Leoshkevich  writes:

> Currently ts_tid contains the parent tid after fork(), which is not
> correct. So far it has not affected anything, but the upcoming
> follow-fork-mode child support relies on the correct value, so fix it.
>
> Signed-off-by: Ilya Leoshkevich 
> ---
>  bsd-user/main.c   | 1 +
>  linux-user/main.c | 1 +
>  2 files changed, 2 insertions(+)
>
> diff --git a/bsd-user/main.c b/bsd-user/main.c
> index e5efb7b8458..4140edc8311 100644
> --- a/bsd-user/main.c
> +++ b/bsd-user/main.c
> @@ -127,6 +127,7 @@ void fork_end(int child)
>   * state, so we don't need to end_exclusive() here.
>   */
>  qemu_init_cpu_list();
> +((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
>  gdbserver_fork(thread_cpu);
>  } else {
>  mmap_fork_end(child);
> diff --git a/linux-user/main.c b/linux-user/main.c
> index 74b2fbb3938..e6427d72332 100644
> --- a/linux-user/main.c
> +++ b/linux-user/main.c
> @@ -160,6 +160,7 @@ void fork_end(int child)
>  }
>  }
>  qemu_init_cpu_list();
> +((TaskState *)thread_cpu->opaque)->ts_tid = qemu_get_thread_id();
>  gdbserver_fork(thread_cpu);
>  } else {
>  cpu_list_unlock();

Given how many functions do this cast dance it does make we wonder if we
should just have a helper for *-user:

  TaskState * get_task_state(CPUState *cs)
  {
return (TaskState *) cs->opaque;
  }

and be done with it. Richard?

Anyway good enough for now:

Reviewed-by: Alex Bennée 


-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro



Re: [RFC PATCH 14/14] migration: Fix return-path thread exit

2024-02-16 Thread Fabiano Rosas
Cédric Le Goater  writes:

> Hello Fabiano
>
> On 2/14/24 21:35, Fabiano Rosas wrote:
>> Cédric Le Goater  writes:
>> 
>>> Hello Fabiano
>>>
>>> On 2/8/24 14:29, Fabiano Rosas wrote:
 Cédric Le Goater  writes:

> In case of error, close_return_path_on_source() can perform a shutdown
> to exit the return-path thread.  However, in migrate_fd_cleanup(),
> 'to_dst_file' is closed before calling close_return_path_on_source()
> and the shutdown fails, leaving the source and destination waiting for
> an event to occur.

 Hi, Cédric

 Are you sure this is not caused by patch 13?
>>>
>>> It happens with upstream QEMU without any patch.
>> 
>> I might have taken that "shutdown fails" in the commit message too
>> literaly. Anyway, I have a proposed solution:
>> 
>> -->8--
>>  From 729aa7b5b7f130f756d41649fdd0862bd2e90430 Mon Sep 17 00:00:00 2001
>> From: Fabiano Rosas 
>> Date: Wed, 14 Feb 2024 16:45:43 -0300
>> Subject: [PATCH] migration: Join the return path thread before releasing
>>   to_dst_file
>> MIME-Version: 1.0
>> Content-Type: text/plain; charset=UTF-8
>> Content-Transfer-Encoding: 8bit
>> 
>> The return path thread might hang at a blocking system call. Before
>> joining the thread we might need to issue a shutdown() on the socket
>> file descriptor to release it. To determine whether the shutdown() is
>> necessary we look at the QEMUFile error.
>> 
>> Make sure we only clean up the QEMUFile after the return path has been
>> waited for.
>
> Yes. That's the important part.
>
>> This fixes a hang when qemu_savevm_state_setup() produced an error
>> that was detected by migration_detect_error(). That skips
>> migration_completion() so close_return_path_on_source() would get
>> stuck waiting for the RP thread to terminate.
>> 
>> At migrate_fd_cleanup() I'm keeping the relative order of joining the
>> migration thread and the return path just in case.
>
> That doesn't look necessary.

Indeed. But I don't trust the migration code, it's full of undocumented
dependencies like that.

> What was the reason to join the migration thread only when
> s->to_dst_file is valid ?

I didn't find any explicit reason looking through the history. It seems
we used to rely on to_dst_file before migration_thread_running was
introduced.

I wouldn't mind keeping that 'if' there.

Let's see what Peter thinks about it.




Re: [PATCH v2 2/3] hw/cxl/cxl-mailbox-utils: Add device patrol scrub control feature

2024-02-16 Thread fan
On Fri, Feb 16, 2024 at 10:16:12AM +, Shiju Jose wrote:
> Hi Fan,
> 
> >-Original Message-
> >From: fan 
> >Sent: 15 February 2024 18:47
> >To: Shiju Jose 
> >Cc: qemu-devel@nongnu.org; linux-...@vger.kernel.org; Jonathan Cameron
> >; tanxiaofei ;
> >Zengtao (B) ; Linuxarm ;
> >fan...@samsung.com
> >Subject: Re: [PATCH v2 2/3] hw/cxl/cxl-mailbox-utils: Add device patrol scrub
> >control feature
> >
> >On Fri, Nov 24, 2023 at 09:53:36PM +0800, shiju.j...@huawei.com wrote:
> >> From: Shiju Jose 
> >>
> >> CXL spec 3.1 section 8.2.9.9.11.1 describes the device patrol scrub
> >> control feature. The device patrol scrub proactively locates and makes
> >> corrections to errors in regular cycle. The patrol scrub control
> >> allows the request to configure patrol scrub input configurations.
> >>
> >> The patrol scrub control allows the requester to specify the number of
> >> hours for which the patrol scrub cycles must be completed, provided
> >> that the requested number is not less than the minimum number of hours
> >> for the patrol scrub cycle that the device is capable of. In addition,
> >> the patrol scrub controls allow the host to disable and enable the
> >> feature in case disabling of the feature is needed for other purposes
> >> such as performance-aware operations which require the background
> >> operations to be turned off.
> >>
> >> Reviewed-by: Davidlohr Bueso 
> >> Signed-off-by: Shiju Jose 
> >> ---
> >
> >LGTM except for some minor comments inlined.
> >
> >
> >>  hw/cxl/cxl-mailbox-utils.c | 97
> >> +-
> >>  1 file changed, 96 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c
> >> index 1bbc9a48a6..5a6f4e4029 100644
> >> --- a/hw/cxl/cxl-mailbox-utils.c
> >> +++ b/hw/cxl/cxl-mailbox-utils.c
> >> @@ -809,6 +809,7 @@ typedef struct CXLSupportedFeatureEntry {  }
> >> QEMU_PACKED CXLSupportedFeatureEntry;
> >>
> >>  enum CXL_SUPPORTED_FEATURES_LIST {
> >> +CXL_FEATURE_PATROL_SCRUB = 0,
> >>  CXL_FEATURE_MAX
> >>  };
> >>
> >> @@ -849,6 +850,37 @@ enum CXL_SET_FEATURE_FLAG_DATA_TRANSFER {
> >>  CXL_SET_FEATURE_FLAG_DATA_TRANSFER_MAX
> >>  };
> >>
> >> +/* CXL r3.1 section 8.2.9.9.11.1: Device Patrol Scrub Control Feature
> >> +*/ static const QemuUUID patrol_scrub_uuid = {
> >> +.data = UUID(0x96dad7d6, 0xfde8, 0x482b, 0xa7, 0x33,
> >> + 0x75, 0x77, 0x4e, 0x06, 0xdb, 0x8a) };
> >> +
> >> +#define CXL_MEMDEV_PS_GET_FEATURE_VERSION0x01
> >> +#define CXL_MEMDEV_PS_SET_FEATURE_VERSION0x01
> >> +#define CXL_MEMDEV_PS_SCRUB_CYCLE_CHANGE_CAP_DEFAULTBIT(0)
> >> +#define CXL_MEMDEV_PS_SCRUB_REALTIME_REPORT_CAP_DEFAULT
> >BIT(1)
> >> +#define CXL_MEMDEV_PS_CUR_SCRUB_CYCLE_DEFAULT12
> >> +#define CXL_MEMDEV_PS_MIN_SCRUB_CYCLE_DEFAULT1
> >> +#define CXL_MEMDEV_PS_ENABLE_DEFAULT0
> >> +
> >> +/* CXL memdev patrol scrub control attributes */ struct
> >> +CXLMemPatrolScrubReadAttrbs {
> >> +uint8_t scrub_cycle_cap;
> >> +uint16_t scrub_cycle;
> >> +uint8_t scrub_flags;
> >> +} QEMU_PACKED cxl_memdev_ps_feat_read_attrbs;
> >> +
> >> +typedef struct CXLMemPatrolScrubWriteAttrbs {
> >> +uint8_t scrub_cycle_hr;
> >> +uint8_t scrub_flags;
> >> +} QEMU_PACKED CXLMemPatrolScrubWriteAttrbs;
> >> +
> >> +typedef struct CXLMemPatrolScrubSetFeature {
> >> +CXLSetFeatureInHeader hdr;
> >> +CXLMemPatrolScrubWriteAttrbs feat_data; } QEMU_PACKED
> >> +QEMU_ALIGNED(16) CXLMemPatrolScrubSetFeature;
> >> +
> >>  /* CXL r3.0 section 8.2.9.6.1: Get Supported Features (Opcode 0500h)
> >> */  static CXLRetCode cmd_features_get_supported(const struct cxl_cmd
> >*cmd,
> >>   uint8_t *payload_in, @@
> >> -872,7 +904,7 @@ static CXLRetCode cmd_features_get_supported(const
> >struct cxl_cmd *cmd,
> >>  uint16_t feat_entries = 0;
> >>
> >>  if (get_feats_in->count < sizeof(CXLSupportedFeatureHeader) ||
> >> -get_feats_in->start_index > CXL_FEATURE_MAX) {
> >> +get_feats_in->start_index >= CXL_FEATURE_MAX) {
> >
> >Not totally sure about this, the spec says "...Greater than..." although I 
> >also think
> >it should be >=. Similar things for the offset usage below.
> 
> Spec r3.1 described in Table 8-95. Get Supported Features Input Payload  as , 
> "Starting Feature Index: Index of the first requested Supported Feature Entry.
> Feature index is a zero-based value."
> Thus I believe get_feats_in->start_index >= CXL_FEATURE_MAX  is correct  
> because
> the feature index is zero-based value.
That is also my understanding.
> 
> Regarding the offset usage mentioned, can you point which code?
> Is it get_feature->offset? 
> 
Yea. Maybe not an issue as long as we keep all consistent.o

Fan
> >
> >Fan
> >
> >>  return CXL_MBOX_INVALID_INPUT;
> >>  }
> >>  req_entries = (get_feats_in->count - @@ -884,6 +916,31 @@ static
> >> CXLRetCode 

RE: [PATCH v3 1/3] Hexagon (target/hexagon) Pass P0 explicitly to helpers that need it

2024-02-16 Thread Brian Cain


> -Original Message-
> From: Taylor Simpson 
> Sent: Tuesday, February 13, 2024 10:27 PM
> To: qemu-devel@nongnu.org
> Cc: Brian Cain ; Matheus Bernardino (QUIC)
> ; Sid Manning ; Marco
> Liebel (QUIC) ; richard.hender...@linaro.org;
> phi...@linaro.org; a...@rev.ng; a...@rev.ng; ltaylorsimp...@gmail.com
> Subject: [PATCH v3 1/3] Hexagon (target/hexagon) Pass P0 explicitly to helpers
> that need it
> 
> WARNING: This email originated from outside of Qualcomm. Please be wary of
> any links or attachments, and do not enable macros.
> 
> Rather than reading P0 from the env, pass it explicitly
> 
> Signed-off-by: Taylor Simpson 
> Reviewed-by: Anton Johansson 
> Tested-by: Anton Johansson 
> ---

Reviewed-by: Brian Cain 

>  target/hexagon/macros.h  |  4 ++--
>  target/hexagon/hex_common.py | 12 +++-
>  2 files changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h
> index 1376d6ccc1..aedc863fab 100644
> --- a/target/hexagon/macros.h
> +++ b/target/hexagon/macros.h
> @@ -1,5 +1,5 @@
>  /*
> - *  Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights
> Reserved.
> + *  Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights
> Reserved.
>   *
>   *  This program is free software; you can redistribute it and/or modify
>   *  it under the terms of the GNU General Public License as published by
> @@ -358,7 +358,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val,
> int shift)
>  #endif
>  #define fREAD_PC() (PC)
> 
> -#define fREAD_P0() (env->pred[0])
> +#define fREAD_P0() (P0)
> 
>  #define fCHECK_PCALIGN(A)
> 
> diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py
> index 195620c7ec..14dcf261b4 100755
> --- a/target/hexagon/hex_common.py
> +++ b/target/hexagon/hex_common.py
> @@ -1,7 +1,7 @@
>  #!/usr/bin/env python3
> 
>  ##
> -##  Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights
> Reserved.
> +##  Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights
> Reserved.
>  ##
>  ##  This program is free software; you can redistribute it and/or modify
>  ##  it under the terms of the GNU General Public License as published by
> @@ -197,6 +197,10 @@ def get_tagimms():
>  return dict(zip(tags, list(map(compute_tag_immediates, tags
> 
> 
> +def need_p0(tag):
> +return "A_IMPLICIT_READS_P0" in attribdict[tag]
> +
> +
>  def need_slot(tag):
>  if (
>  "A_CVI_SCATTER" not in attribdict[tag]
> @@ -1118,6 +1122,12 @@ def helper_args(tag, regs, imms):
>  "tcg_constant_tl(ctx->next_PC)",
>  "target_ulong next_PC"
>  ))
> +if need_p0(tag):
> +args.append(HelperArg(
> +"i32",
> +"hex_pred[0]",
> +"uint32_t P0"
> +))
>  if need_slot(tag):
>  args.append(HelperArg(
>  "i32",
> --
> 2.34.1



RE: [PATCH v3 2/3] Hexagon (target/hexagon) Pass SP explicitly to helpers that need it

2024-02-16 Thread Brian Cain


> -Original Message-
> From: Taylor Simpson 
> Sent: Tuesday, February 13, 2024 10:27 PM
> To: qemu-devel@nongnu.org
> Cc: Brian Cain ; Matheus Bernardino (QUIC)
> ; Sid Manning ; Marco
> Liebel (QUIC) ; richard.hender...@linaro.org;
> phi...@linaro.org; a...@rev.ng; a...@rev.ng; ltaylorsimp...@gmail.com
> Subject: [PATCH v3 2/3] Hexagon (target/hexagon) Pass SP explicitly to helpers
> that need it
> 
> WARNING: This email originated from outside of Qualcomm. Please be wary of
> any links or attachments, and do not enable macros.
> 
> Rather than reading SP from the env, pass it explicitly
> 
> Signed-off-by: Taylor Simpson 
> Reviewed-by: Anton Johansson 
> Tested-by: Anton Johansson 
> ---

Reviewed-by: Brian Cain 

>  target/hexagon/macros.h  |  2 +-
>  target/hexagon/attribs_def.h.inc |  3 ++-
>  target/hexagon/hex_common.py | 11 +++
>  3 files changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h
> index aedc863fab..feb798c6c0 100644
> --- a/target/hexagon/macros.h
> +++ b/target/hexagon/macros.h
> @@ -343,7 +343,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val,
> int shift)
> 
>  #define fREAD_LR() (env->gpr[HEX_REG_LR])
> 
> -#define fREAD_SP() (env->gpr[HEX_REG_SP])
> +#define fREAD_SP() (SP)
>  #define fREAD_LC0 (env->gpr[HEX_REG_LC0])
>  #define fREAD_LC1 (env->gpr[HEX_REG_LC1])
>  #define fREAD_SA0 (env->gpr[HEX_REG_SA0])
> diff --git a/target/hexagon/attribs_def.h.inc 
> b/target/hexagon/attribs_def.h.inc
> index 87942d46f4..9e3a05f882 100644
> --- a/target/hexagon/attribs_def.h.inc
> +++ b/target/hexagon/attribs_def.h.inc
> @@ -1,5 +1,5 @@
>  /*
> - *  Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights
> Reserved.
> + *  Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights
> Reserved.
>   *
>   *  This program is free software; you can redistribute it and/or modify
>   *  it under the terms of the GNU General Public License as published by
> @@ -117,6 +117,7 @@ DEF_ATTRIB(IMPLICIT_READS_P1, "Reads the P1
> register", "", "")
>  DEF_ATTRIB(IMPLICIT_READS_P2, "Reads the P2 register", "", "")
>  DEF_ATTRIB(IMPLICIT_READS_P3, "Reads the P3 register", "", "")
>  DEF_ATTRIB(IMPLICIT_WRITES_USR, "May write USR", "", "")
> +DEF_ATTRIB(IMPLICIT_READS_SP, "Reads the SP register", "", "")
>  DEF_ATTRIB(COMMUTES, "The operation is communitive", "", "")
>  DEF_ATTRIB(DEALLOCRET, "dealloc_return", "", "")
>  DEF_ATTRIB(DEALLOCFRAME, "deallocframe", "", "")
> diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py
> index 14dcf261b4..b96f67972d 100755
> --- a/target/hexagon/hex_common.py
> +++ b/target/hexagon/hex_common.py
> @@ -101,6 +101,7 @@ def calculate_attribs():
>  add_qemu_macro_attrib('fLSBNEW1', 'A_IMPLICIT_READS_P1')
>  add_qemu_macro_attrib('fLSBNEW1NOT', 'A_IMPLICIT_READS_P1')
>  add_qemu_macro_attrib('fREAD_P3', 'A_IMPLICIT_READS_P3')
> +add_qemu_macro_attrib('fREAD_SP', 'A_IMPLICIT_READS_SP')
> 
>  # Recurse down macros, find attributes from sub-macros
>  macroValues = list(macros.values())
> @@ -201,6 +202,10 @@ def need_p0(tag):
>  return "A_IMPLICIT_READS_P0" in attribdict[tag]
> 
> 
> +def need_sp(tag):
> +return "A_IMPLICIT_READS_SP" in attribdict[tag]
> +
> +
>  def need_slot(tag):
>  if (
>  "A_CVI_SCATTER" not in attribdict[tag]
> @@ -1128,6 +1133,12 @@ def helper_args(tag, regs, imms):
>  "hex_pred[0]",
>  "uint32_t P0"
>  ))
> +if need_sp(tag):
> +args.append(HelperArg(
> +"i32",
> +"hex_gpr[HEX_REG_SP]",
> +"uint32_t SP"
> +))
>  if need_slot(tag):
>  args.append(HelperArg(
>  "i32",
> --
> 2.34.1



RE: [PATCH v3 2/3] Hexagon (target/hexagon) Pass SP explicitly to helpers that need it

2024-02-16 Thread Brian Cain


> -Original Message-
> From: Taylor Simpson 
> Sent: Tuesday, February 13, 2024 10:27 PM
> To: qemu-devel@nongnu.org
> Cc: Brian Cain ; Matheus Bernardino (QUIC)
> ; Sid Manning ; Marco
> Liebel (QUIC) ; richard.hender...@linaro.org;
> phi...@linaro.org; a...@rev.ng; a...@rev.ng; ltaylorsimp...@gmail.com
> Subject: [PATCH v3 2/3] Hexagon (target/hexagon) Pass SP explicitly to helpers
> that need it
> 
> WARNING: This email originated from outside of Qualcomm. Please be wary of
> any links or attachments, and do not enable macros.
> 
> Rather than reading SP from the env, pass it explicitly
> 
> Signed-off-by: Taylor Simpson 
> Reviewed-by: Anton Johansson 
> Tested-by: Anton Johansson 
> ---


Reviewed-by: Brian Cain 

>  target/hexagon/macros.h  |  2 +-
>  target/hexagon/attribs_def.h.inc |  3 ++-
>  target/hexagon/hex_common.py | 11 +++
>  3 files changed, 14 insertions(+), 2 deletions(-)
> 
> diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h
> index aedc863fab..feb798c6c0 100644
> --- a/target/hexagon/macros.h
> +++ b/target/hexagon/macros.h
> @@ -343,7 +343,7 @@ static inline TCGv gen_read_ireg(TCGv result, TCGv val,
> int shift)
> 
>  #define fREAD_LR() (env->gpr[HEX_REG_LR])
> 
> -#define fREAD_SP() (env->gpr[HEX_REG_SP])
> +#define fREAD_SP() (SP)
>  #define fREAD_LC0 (env->gpr[HEX_REG_LC0])
>  #define fREAD_LC1 (env->gpr[HEX_REG_LC1])
>  #define fREAD_SA0 (env->gpr[HEX_REG_SA0])
> diff --git a/target/hexagon/attribs_def.h.inc 
> b/target/hexagon/attribs_def.h.inc
> index 87942d46f4..9e3a05f882 100644
> --- a/target/hexagon/attribs_def.h.inc
> +++ b/target/hexagon/attribs_def.h.inc
> @@ -1,5 +1,5 @@
>  /*
> - *  Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights
> Reserved.
> + *  Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights
> Reserved.
>   *
>   *  This program is free software; you can redistribute it and/or modify
>   *  it under the terms of the GNU General Public License as published by
> @@ -117,6 +117,7 @@ DEF_ATTRIB(IMPLICIT_READS_P1, "Reads the P1
> register", "", "")
>  DEF_ATTRIB(IMPLICIT_READS_P2, "Reads the P2 register", "", "")
>  DEF_ATTRIB(IMPLICIT_READS_P3, "Reads the P3 register", "", "")
>  DEF_ATTRIB(IMPLICIT_WRITES_USR, "May write USR", "", "")
> +DEF_ATTRIB(IMPLICIT_READS_SP, "Reads the SP register", "", "")
>  DEF_ATTRIB(COMMUTES, "The operation is communitive", "", "")
>  DEF_ATTRIB(DEALLOCRET, "dealloc_return", "", "")
>  DEF_ATTRIB(DEALLOCFRAME, "deallocframe", "", "")
> diff --git a/target/hexagon/hex_common.py b/target/hexagon/hex_common.py
> index 14dcf261b4..b96f67972d 100755
> --- a/target/hexagon/hex_common.py
> +++ b/target/hexagon/hex_common.py
> @@ -101,6 +101,7 @@ def calculate_attribs():
>  add_qemu_macro_attrib('fLSBNEW1', 'A_IMPLICIT_READS_P1')
>  add_qemu_macro_attrib('fLSBNEW1NOT', 'A_IMPLICIT_READS_P1')
>  add_qemu_macro_attrib('fREAD_P3', 'A_IMPLICIT_READS_P3')
> +add_qemu_macro_attrib('fREAD_SP', 'A_IMPLICIT_READS_SP')
> 
>  # Recurse down macros, find attributes from sub-macros
>  macroValues = list(macros.values())
> @@ -201,6 +202,10 @@ def need_p0(tag):
>  return "A_IMPLICIT_READS_P0" in attribdict[tag]
> 
> 
> +def need_sp(tag):
> +return "A_IMPLICIT_READS_SP" in attribdict[tag]
> +
> +
>  def need_slot(tag):
>  if (
>  "A_CVI_SCATTER" not in attribdict[tag]
> @@ -1128,6 +1133,12 @@ def helper_args(tag, regs, imms):
>  "hex_pred[0]",
>  "uint32_t P0"
>  ))
> +if need_sp(tag):
> +args.append(HelperArg(
> +"i32",
> +"hex_gpr[HEX_REG_SP]",
> +"uint32_t SP"
> +))
>  if need_slot(tag):
>  args.append(HelperArg(
>  "i32",
> --
> 2.34.1



Re: [PATCH 1/6] hw/arm: Inline sysbus_create_simple(PL110 / PL111)

2024-02-16 Thread BALATON Zoltan

On Fri, 16 Feb 2024, Philippe Mathieu-Daudé wrote:

We want to set another qdev property (a link) for the pl110
and pl111 devices, we can not use sysbus_create_simple() which
only passes sysbus base address and IRQs as arguments. Inline
it so we can set the link property in the next commit.

Signed-off-by: Philippe Mathieu-Daudé 
---
hw/arm/realview.c|  5 -
hw/arm/versatilepb.c |  6 +-
hw/arm/vexpress.c| 10 --
3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index 9058f5b414..77300e92e5 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -238,7 +238,10 @@ static void realview_init(MachineState *machine,
sysbus_create_simple("pl061", 0x10014000, pic[7]);
gpio2 = sysbus_create_simple("pl061", 0x10015000, pic[8]);

-sysbus_create_simple("pl111", 0x1002, pic[23]);
+dev = qdev_new("pl111");
+sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), _fatal);
+sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x1002);
+sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[23]);


Not directly related to this patch but this blows up 1 line into 4 just to 
allow setting a property. Maybe just to keep some simplicity we'd rather 
need either a sysbus_realize_simple function that takes a sysbus device 
instead of the name and does not create the device itself or some way to 
pass properties to sysbus create simple (but the latter may not be easy to 
do in a generic way so not sure about that). What do you think?


Regards,
BALATON Zoltan

Re: [PATCH v3 01/11] gdbstub: Support disablement in a multi-threaded process

2024-02-16 Thread Alex Bennée
Ilya Leoshkevich  writes:

> The upcoming follow-fork-mode child support will require disabling
> gdbstub in the parent process, which may have multiple threads (which
> are represented as CPUs).
>
> Loop over all CPUs in order to remove breakpoints and disable
> single-step. Move the respective code into a separate function.
>
> Signed-off-by: Ilya Leoshkevich 

Reviewed-by: Alex Bennée 

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro



[PATCH 23/23] docs/devel: plugins can trigger a tb flush

2024-02-16 Thread Alex Bennée
From: Pierrick Bouvier 

When scoreboards need to be reallocated.

Signed-off-by: Pierrick Bouvier 
Reviewed-by: Richard Henderson 
Message-Id: <20240213094009.150349-8-pierrick.bouv...@linaro.org>
Signed-off-by: Alex Bennée 
---
 docs/devel/multi-thread-tcg.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/devel/multi-thread-tcg.rst b/docs/devel/multi-thread-tcg.rst
index 7302c3bf534..1420789fff3 100644
--- a/docs/devel/multi-thread-tcg.rst
+++ b/docs/devel/multi-thread-tcg.rst
@@ -109,6 +109,7 @@ including:
   - debugging operations (breakpoint insertion/removal)
   - some CPU helper functions
   - linux-user spawning its first thread
+  - operations related to TCG Plugins
 
 This is done with the async_safe_run_on_cpu() mechanism to ensure all
 vCPUs are quiescent when changes are being made to shared global
-- 
2.39.2




[PATCH 21/23] docs/devel: lift example and plugin API sections up

2024-02-16 Thread Alex Bennée
This makes them a bit more visible in the TCG emulation menu rather
than hiding them away bellow the ToC limit.

Message-Id: <20240103173349.398526-43-alex.ben...@linaro.org>
Reviewed-by: Pierrick Bouvier 
Signed-off-by: Alex Bennée 
Reviewed-by: Philippe Mathieu-Daudé 
---
 docs/devel/tcg-plugins.rst | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst
index fa7421279f5..535a74684c5 100644
--- a/docs/devel/tcg-plugins.rst
+++ b/docs/devel/tcg-plugins.rst
@@ -143,7 +143,7 @@ requested. The plugin isn't completely uninstalled until 
the safe work
 has executed while all vCPUs are quiescent.
 
 Example Plugins

+===
 
 There are a number of plugins included with QEMU and you are
 encouraged to contribute your own plugins plugins upstream. There is a
@@ -591,8 +591,8 @@ The plugin has a number of arguments, all of them are 
optional:
   configuration arguments implies ``l2=on``.
   (default: N = 2097152 (2MB), B = 64, A = 16)
 
-API

+Plugin API
+==
 
 The following API is generated from the inline documentation in
 ``include/qemu/qemu-plugin.h``. Please ensure any updates to the API
-- 
2.39.2




[PATCH 18/23] plugins: add an API to read registers

2024-02-16 Thread Alex Bennée
We can only request a list of registers once the vCPU has been
initialised so the user needs to use either call the get function on
vCPU initialisation or during the translation phase.

We don't expose the reg number to the plugin instead hiding it behind
an opaque handle. This allows for a bit of future proofing should the
internals need to be changed while also being hashed against the
CPUClass so we can handle different register sets per-vCPU in
hetrogenous situations.

Having an internal state within the plugins also allows us to expand
the interface in future (for example providing callbacks on register
change if the translator can track changes).

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1706
Cc: Akihiko Odaki 
Message-Id: <20240103173349.398526-39-alex.ben...@linaro.org>
Based-on: <20231025093128.33116-18-akihiko.od...@daynix.com>
Signed-off-by: Alex Bennée 
Reviewed-by: Pierrick Bouvier 

---
v4
  - the get/read_registers functions are now implicitly for current
  vCPU only to accidental cpu != current_cpu uses.
---
 include/qemu/qemu-plugin.h   |  48 +++-
 plugins/api.c| 107 +++
 plugins/qemu-plugins.symbols |   2 +
 3 files changed, 155 insertions(+), 2 deletions(-)

diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 93981f8f89f..3b6b18058d2 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -11,6 +11,7 @@
 #ifndef QEMU_QEMU_PLUGIN_H
 #define QEMU_QEMU_PLUGIN_H
 
+#include 
 #include 
 #include 
 #include 
@@ -229,8 +230,8 @@ struct qemu_plugin_insn;
  * @QEMU_PLUGIN_CB_R_REGS: callback reads the CPU's regs
  * @QEMU_PLUGIN_CB_RW_REGS: callback reads and writes the CPU's regs
  *
- * Note: currently unused, plugins cannot read or change system
- * register state.
+ * Note: currently QEMU_PLUGIN_CB_RW_REGS is unused, plugins cannot change
+ * system register state.
  */
 enum qemu_plugin_cb_flags {
 QEMU_PLUGIN_CB_NO_REGS,
@@ -707,4 +708,47 @@ uint64_t qemu_plugin_end_code(void);
 QEMU_PLUGIN_API
 uint64_t qemu_plugin_entry_code(void);
 
+/** struct qemu_plugin_register - Opaque handle for register access */
+struct qemu_plugin_register;
+
+/**
+ * typedef qemu_plugin_reg_descriptor - register descriptions
+ *
+ * @handle: opaque handle for retrieving value with qemu_plugin_read_register
+ * @name: register name
+ * @feature: optional feature descriptor, can be NULL
+ */
+typedef struct {
+struct qemu_plugin_register *handle;
+const char *name;
+const char *feature;
+} qemu_plugin_reg_descriptor;
+
+/**
+ * qemu_plugin_get_registers() - return register list for current vCPU
+ *
+ * Returns a GArray of qemu_plugin_reg_descriptor or NULL. Caller
+ * frees the array (but not the const strings).
+ *
+ * Should be used from a qemu_plugin_register_vcpu_init_cb() callback
+ * after the vCPU is initialised, i.e. in the vCPU context.
+ */
+GArray *qemu_plugin_get_registers(void);
+
+/**
+ * qemu_plugin_read_register() - read register for current vCPU
+ *
+ * @handle: a @qemu_plugin_reg_handle handle
+ * @buf: A GByteArray for the data owned by the plugin
+ *
+ * This function is only available in a context that register read access is
+ * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag.
+ *
+ * Returns the size of the read register. The content of @buf is in target byte
+ * order. On failure returns -1
+ */
+int qemu_plugin_read_register(struct qemu_plugin_register *handle,
+  GByteArray *buf);
+
+
 #endif /* QEMU_QEMU_PLUGIN_H */
diff --git a/plugins/api.c b/plugins/api.c
index 54df72c1c00..483f04e85e4 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -8,6 +8,7 @@
  *
  *  qemu_plugin_tb
  *  qemu_plugin_insn
+ *  qemu_plugin_register
  *
  * Which can then be passed back into the API to do additional things.
  * As such all the public functions in here are exported in
@@ -35,10 +36,12 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/main-loop.h"
 #include "qemu/plugin.h"
 #include "qemu/log.h"
 #include "tcg/tcg.h"
 #include "exec/exec-all.h"
+#include "exec/gdbstub.h"
 #include "exec/ram_addr.h"
 #include "disas/disas.h"
 #include "plugin.h"
@@ -410,3 +413,107 @@ uint64_t qemu_plugin_entry_code(void)
 #endif
 return entry;
 }
+
+/*
+ * Register handles
+ *
+ * The plugin infrastructure keeps hold of these internal data
+ * structures which are presented to plugins as opaque handles. They
+ * are global to the system and therefor additions to the hash table
+ * must be protected by the @reg_handle_lock.
+ *
+ * In order to future proof for up-coming heterogeneous work we want
+ * different entries for each CPU type while sharing them in the
+ * common case of multiple cores of the same type.
+ */
+
+static QemuMutex reg_handle_lock;
+
+struct qemu_plugin_register {
+const char *name;
+int gdb_reg_num;
+};
+
+static GHashTable *reg_handles; /* hash table of PluginReg */
+
+/* Generate a stable key - would xxhash be 

[PATCH 15/23] cpu: call plugin init hook asynchronously

2024-02-16 Thread Alex Bennée
From: Pierrick Bouvier 

This ensures we run during a cpu_exec, which allows to call start/end
exclusive from this init hook (needed for new scoreboard API introduced
later).

async work is run before any tb is translated/executed, so we can
guarantee plugin init will be called before any other hook.

The previous change made sure that any idle/resume cb call will not be
done before initializing plugin for a given vcpu.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
Message-Id: <20240213094009.150349-5-pierrick.bouv...@linaro.org>
Signed-off-by: Alex Bennée 
---
 hw/core/cpu-common.c | 9 +++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index fe16d0d9df8..68786360ea5 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -194,6 +194,11 @@ static void cpu_common_parse_features(const char 
*typename, char *features,
 }
 }
 
+static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused)
+{
+qemu_plugin_vcpu_init_hook(cpu);
+}
+
 static void cpu_common_realizefn(DeviceState *dev, Error **errp)
 {
 CPUState *cpu = CPU(dev);
@@ -217,9 +222,9 @@ static void cpu_common_realizefn(DeviceState *dev, Error 
**errp)
 cpu_resume(cpu);
 }
 
-/* Plugin initialization must wait until the cpu is fully realized. */
+/* Plugin initialization must wait until the cpu start executing code */
 if (tcg_enabled()) {
-qemu_plugin_vcpu_init_hook(cpu);
+async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL);
 }
 
 /* NOTE: latest generic point where the cpu is fully realized */
-- 
2.39.2




[PATCH 20/23] contrib/plugins: extend execlog to track register changes

2024-02-16 Thread Alex Bennée
With the new plugin register API we can now track changes to register
values. Currently the implementation is fairly dumb which will slow
down if a large number of register values are being tracked. This
could be improved by only instrumenting instructions which mention
registers we are interested in tracking.

Example usage:

  ./qemu-aarch64 -D plugin.log -d plugin \
 -cpu max,sve256=on \
 -plugin contrib/plugins/libexeclog.so,reg=sp,reg=z\* \
 ./tests/tcg/aarch64-linux-user/sha512-sve

will display in the execlog any changes to the stack pointer (sp) and
the SVE Z registers.

As testing registers every instruction will be quite a heavy operation
there is an additional flag which attempts to optimise the register
tracking by only instrumenting instructions which are likely to change
its value. This relies on the QEMU disassembler showing up the register
names in disassembly so is an explicit opt-in.

Message-Id: <20240103173349.398526-41-alex.ben...@linaro.org>
Signed-off-by: Alex Bennée 
Cc: Akihiko Odaki 
Based-On: <20231025093128.33116-19-akihiko.od...@daynix.com>

---
v3
  - just use a GArray for the CPU array
  - drop duplicate of cpu_index
v4
  - rebase and api fixups
  - I accidentally squashed the optimisation last round so update
  commit message with the details.
---
 docs/devel/tcg-plugins.rst |  17 +-
 contrib/plugins/execlog.c  | 316 +++--
 2 files changed, 281 insertions(+), 52 deletions(-)

diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst
index 81dcd43a612..fa7421279f5 100644
--- a/docs/devel/tcg-plugins.rst
+++ b/docs/devel/tcg-plugins.rst
@@ -497,6 +497,22 @@ arguments if required::
   $ qemu-system-arm $(QEMU_ARGS) \
 -plugin ./contrib/plugins/libexeclog.so,ifilter=st1w,afilter=0x40001808 -d 
plugin
 
+This plugin can also dump registers when they change value. Specify the name 
of the
+registers with multiple ``reg`` options. You can also use glob style matching 
if you wish::
+
+  $ qemu-system-arm $(QEMU_ARGS) \
+-plugin ./contrib/plugins/libexeclog.so,reg=\*_el2,reg=sp -d plugin
+
+Be aware that each additional register to check will slow down
+execution quite considerably. You can optimise the number of register
+checks done by using the rdisas option. This will only instrument
+instructions that mention the registers in question in disassembly.
+This is not foolproof as some instructions implicitly change
+instructions. You can use the ifilter to catch these cases:
+
+  $ qemu-system-arm $(QEMU_ARGS) \
+-plugin 
./contrib/plugins/libexeclog.so,ifilter=msr,ifilter=blr,reg=x30,reg=\*_el1,rdisas=on
+
 - contrib/plugins/cache.c
 
 Cache modelling plugin that measures the performance of a given L1 cache
@@ -583,4 +599,3 @@ The following API is generated from the inline 
documentation in
 include the full kernel-doc annotations.
 
 .. kernel-doc:: include/qemu/qemu-plugin.h
-
diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c
index f262eeb..dd7168cb548 100644
--- a/contrib/plugins/execlog.c
+++ b/contrib/plugins/execlog.c
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2021, Alexandre Iooss 
  *
- * Log instruction execution with memory access.
+ * Log instruction execution with memory access and register changes
  *
  * License: GNU GPL, version 2 or later.
  *   See the COPYING file in the top-level directory.
@@ -15,29 +15,40 @@
 
 #include 
 
+typedef struct {
+struct qemu_plugin_register *handle;
+GByteArray *last;
+GByteArray *new;
+const char *name;
+} Register;
+
+typedef struct CPU {
+/* Store last executed instruction on each vCPU as a GString */
+GString *last_exec;
+/* Ptr array of Register */
+GPtrArray *registers;
+} CPU;
+
 QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
 
-/* Store last executed instruction on each vCPU as a GString */
-static GPtrArray *last_exec;
+static GArray *cpus;
 static GRWLock expand_array_lock;
 
 static GPtrArray *imatches;
 static GArray *amatches;
+static GPtrArray *rmatches;
+static bool disas_assist;
+static GMutex add_reg_name_lock;
+static GPtrArray *all_reg_names;
 
-/*
- * Expand last_exec array.
- *
- * As we could have multiple threads trying to do this we need to
- * serialise the expansion under a lock.
- */
-static void expand_last_exec(int cpu_index)
+static CPU *get_cpu(int vcpu_index)
 {
-g_rw_lock_writer_lock(_array_lock);
-while (cpu_index >= last_exec->len) {
-GString *s = g_string_new(NULL);
-g_ptr_array_add(last_exec, s);
-}
-g_rw_lock_writer_unlock(_array_lock);
+CPU *c;
+g_rw_lock_reader_lock(_array_lock);
+c = _array_index(cpus, CPU, vcpu_index);
+g_rw_lock_reader_unlock(_array_lock);
+
+return c;
 }
 
 /**
@@ -46,13 +57,10 @@ static void expand_last_exec(int cpu_index)
 static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t info,
  uint64_t vaddr, void *udata)
 {
-GString *s;
+CPU *c = 

[PATCH 22/23] docs/devel: document some plugin assumptions

2024-02-16 Thread Alex Bennée
While we attempt to hide implementation details from the plugin we
shouldn't be totally obtuse. Let the user know what they can and can't
expect with the various instrumentation options.

Message-Id: <20240103173349.398526-44-alex.ben...@linaro.org>
Reviewed-by: Pierrick Bouvier 
Signed-off-by: Alex Bennée 
---
 docs/devel/tcg-plugins.rst | 49 ++
 1 file changed, 49 insertions(+)

diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst
index 535a74684c5..9cc09d8c3da 100644
--- a/docs/devel/tcg-plugins.rst
+++ b/docs/devel/tcg-plugins.rst
@@ -112,6 +112,55 @@ details are opaque to plugins. The plugin is able to query 
select
 details of instructions and system configuration only through the
 exported *qemu_plugin* functions.
 
+However the following assumptions can be made:
+
+Translation Blocks
+++
+
+All code will go through a translation phase although not all
+translations will be necessarily be executed. You need to instrument
+actual executions to track what is happening.
+
+It is quite normal to see the same address translated multiple times.
+If you want to track the code in system emulation you should examine
+the underlying physical address (``qemu_plugin_insn_haddr``) to take
+into account the effects of virtual memory although if the system does
+paging this will change too.
+
+Not all instructions in a block will always execute so if its
+important to track individual instruction execution you need to
+instrument them directly. However asynchronous interrupts will not
+change control flow mid-block.
+
+Instructions
+
+
+Instruction instrumentation runs before the instruction executes. You
+can be can be sure the instruction will be dispatched, but you can't
+be sure it will complete. Generally this will be because of a
+synchronous exception (e.g. SIGILL) triggered by the instruction
+attempting to execute. If you want to be sure you will need to
+instrument the next instruction as well. See the ``execlog.c`` plugin
+for examples of how to track this and finalise details after execution.
+
+Memory Accesses

+
+Memory callbacks are called after a successful load or store.
+Unsuccessful operations (i.e. faults) will not be visible to memory
+instrumentation although the execution side effects can be observed
+(e.g. entering a exception handler).
+
+System Idle and Resume States
++
+
+The ``qemu_plugin_register_vcpu_idle_cb`` and
+``qemu_plugin_register_vcpu_resume_cb`` functions can be used to track
+when CPUs go into and return from sleep states when waiting for
+external I/O. Be aware though that these may occur less frequently
+than in real HW due to the inefficiencies of emulation giving less
+chance for the CPU to idle.
+
 Internals
 -
 
-- 
2.39.2




[PATCH 14/23] plugins: fix order of init/idle/resume callback

2024-02-16 Thread Alex Bennée
From: Pierrick Bouvier 

We found that vcpu_init_hook was called *after* idle callback.
vcpu_init is called from cpu_realize_fn, while idle/resume cb are called
from qemu_wait_io_event (in vcpu thread).

This change ensures we only call idle and resume cb only once a plugin
was init for a given vcpu.

Next change in the series will run vcpu_init asynchronously, which will
make it run *after* resume callback as well. So we fix this now.

Reviewed-by: Richard Henderson 
Signed-off-by: Pierrick Bouvier 
Message-Id: <20240213094009.150349-4-pierrick.bouv...@linaro.org>
Signed-off-by: Alex Bennée 
---
 plugins/core.c | 9 +++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/plugins/core.c b/plugins/core.c
index caa66311351..2392bbb8889 100644
--- a/plugins/core.c
+++ b/plugins/core.c
@@ -391,12 +391,17 @@ void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t 
num, int64_t ret)
 
 void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
 {
-plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE);
+/* idle and resume cb may be called before init, ignore in this case */
+if (cpu->cpu_index < plugin.num_vcpus) {
+plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE);
+}
 }
 
 void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
 {
-plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME);
+if (cpu->cpu_index < plugin.num_vcpus) {
+plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME);
+}
 }
 
 void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
-- 
2.39.2




[PATCH 17/23] gdbstub: expose api to find registers

2024-02-16 Thread Alex Bennée
Expose an internal API to QEMU to return all the registers for a vCPU.
The list containing the details required to called gdb_read_register().

Based-on: <20231025093128.33116-15-akihiko.od...@daynix.com>
Cc: Akihiko Odaki 
Message-Id: <20240103173349.398526-38-alex.ben...@linaro.org>
Signed-off-by: Alex Bennée 

---
v3
  - rm unused api functions left over
---
 include/exec/gdbstub.h | 28 
 gdbstub/gdbstub.c  | 27 ++-
 2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index da9ddfe54c5..eb14b91139b 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -111,6 +111,34 @@ void gdb_feature_builder_end(const GDBFeatureBuilder 
*builder);
  */
 const GDBFeature *gdb_find_static_feature(const char *xmlname);
 
+/**
+ * gdb_read_register() - Read a register associated with a CPU.
+ * @cpu: The CPU associated with the register.
+ * @buf: The buffer that the read register will be appended to.
+ * @reg: The register's number returned by gdb_find_feature_register().
+ *
+ * Return: The number of read bytes.
+ */
+int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
+
+/**
+ * typedef GDBRegDesc - a register description from gdbstub
+ */
+typedef struct {
+int gdb_reg;
+const char *name;
+const char *feature_name;
+} GDBRegDesc;
+
+/**
+ * gdb_get_register_list() - Return list of all registers for CPU
+ * @cpu: The CPU being searched
+ *
+ * Returns a GArray of GDBRegDesc, caller frees array but not the
+ * const strings.
+ */
+GArray *gdb_get_register_list(CPUState *cpu);
+
 void gdb_set_stop_cpu(CPUState *cpu);
 
 /* in gdbstub-xml.c, generated by scripts/feature_to_c.py */
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index a55b5e6581a..2909bc8c69f 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -490,7 +490,32 @@ const GDBFeature *gdb_find_static_feature(const char 
*xmlname)
 g_assert_not_reached();
 }
 
-static int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg)
+GArray *gdb_get_register_list(CPUState *cpu)
+{
+GArray *results = g_array_new(true, true, sizeof(GDBRegDesc));
+
+/* registers are only available once the CPU is initialised */
+if (!cpu->gdb_regs) {
+return results;
+}
+
+for (int f = 0; f < cpu->gdb_regs->len; f++) {
+GDBRegisterState *r = _array_index(cpu->gdb_regs, GDBRegisterState, 
f);
+for (int i = 0; i < r->feature->num_regs; i++) {
+const char *name = r->feature->regs[i];
+GDBRegDesc desc = {
+r->base_reg + i,
+name,
+r->feature->name
+};
+g_array_append_val(results, desc);
+}
+}
+
+return results;
+}
+
+int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg)
 {
 CPUClass *cc = CPU_GET_CLASS(cpu);
 GDBRegisterState *r;
-- 
2.39.2




[PATCH 19/23] contrib/plugins: fix imatch

2024-02-16 Thread Alex Bennée
We can't directly save the ephemeral imatch from argv as that memory
will get recycled.

Message-Id: <20240103173349.398526-40-alex.ben...@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé 
Signed-off-by: Alex Bennée 
Reviewed-by: Philippe Mathieu-Daudé 
---
 contrib/plugins/execlog.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c
index 82dc2f584e2..f262eeb 100644
--- a/contrib/plugins/execlog.c
+++ b/contrib/plugins/execlog.c
@@ -199,7 +199,7 @@ static void parse_insn_match(char *match)
 if (!imatches) {
 imatches = g_ptr_array_new();
 }
-g_ptr_array_add(imatches, match);
+g_ptr_array_add(imatches, g_strdup(match));
 }
 
 static void parse_vaddr_match(char *match)
-- 
2.39.2




[PATCH 16/23] plugins: Use different helpers when reading registers

2024-02-16 Thread Alex Bennée
From: Akihiko Odaki 

This avoids optimizations incompatible when reading registers.

Signed-off-by: Akihiko Odaki 
Reviewed-by: Pierrick Bouvier 
Message-Id: <20240103173349.398526-37-alex.ben...@linaro.org>
Message-Id: <20231213-gdb-v17-12-777047380...@daynix.com>
Signed-off-by: Alex Bennée 
Reviewed-by: Richard Henderson 
---
 accel/tcg/plugin-helpers.h |  3 ++-
 include/qemu/plugin.h  |  1 +
 accel/tcg/plugin-gen.c | 43 ++
 plugins/api.c  | 12 +--
 4 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/accel/tcg/plugin-helpers.h b/accel/tcg/plugin-helpers.h
index 8e685e06545..11796436f35 100644
--- a/accel/tcg/plugin-helpers.h
+++ b/accel/tcg/plugin-helpers.h
@@ -1,4 +1,5 @@
 #ifdef CONFIG_PLUGIN
-DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, 
void, i32, ptr)
+DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb_no_wg, TCG_CALL_NO_WG | 
TCG_CALL_PLUGIN, void, i32, ptr)
+DEF_HELPER_FLAGS_2(plugin_vcpu_udata_cb_no_rwg, TCG_CALL_NO_RWG | 
TCG_CALL_PLUGIN, void, i32, ptr)
 DEF_HELPER_FLAGS_4(plugin_vcpu_mem_cb, TCG_CALL_NO_RWG | TCG_CALL_PLUGIN, 
void, i32, i32, i64, ptr)
 #endif
diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
index 7fdc3a4849f..b0c5ac68293 100644
--- a/include/qemu/plugin.h
+++ b/include/qemu/plugin.h
@@ -73,6 +73,7 @@ enum plugin_dyn_cb_type {
 
 enum plugin_dyn_cb_subtype {
 PLUGIN_CB_REGULAR,
+PLUGIN_CB_REGULAR_R,
 PLUGIN_CB_INLINE,
 PLUGIN_N_CB_SUBTYPES,
 };
diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c
index 78b331b2510..b37ce7683e6 100644
--- a/accel/tcg/plugin-gen.c
+++ b/accel/tcg/plugin-gen.c
@@ -79,6 +79,7 @@ enum plugin_gen_from {
 
 enum plugin_gen_cb {
 PLUGIN_GEN_CB_UDATA,
+PLUGIN_GEN_CB_UDATA_R,
 PLUGIN_GEN_CB_INLINE,
 PLUGIN_GEN_CB_MEM,
 PLUGIN_GEN_ENABLE_MEM_HELPER,
@@ -90,7 +91,10 @@ enum plugin_gen_cb {
  * These helpers are stubs that get dynamically switched out for calls
  * direct to the plugin if they are subscribed to.
  */
-void HELPER(plugin_vcpu_udata_cb)(uint32_t cpu_index, void *udata)
+void HELPER(plugin_vcpu_udata_cb_no_wg)(uint32_t cpu_index, void *udata)
+{ }
+
+void HELPER(plugin_vcpu_udata_cb_no_rwg)(uint32_t cpu_index, void *udata)
 { }
 
 void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index,
@@ -98,7 +102,7 @@ void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index,
 void *userdata)
 { }
 
-static void gen_empty_udata_cb(void)
+static void gen_empty_udata_cb(void (*gen_helper)(TCGv_i32, TCGv_ptr))
 {
 TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
 TCGv_ptr udata = tcg_temp_ebb_new_ptr();
@@ -106,12 +110,22 @@ static void gen_empty_udata_cb(void)
 tcg_gen_movi_ptr(udata, 0);
 tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
-gen_helper_plugin_vcpu_udata_cb(cpu_index, udata);
+gen_helper(cpu_index, udata);
 
 tcg_temp_free_ptr(udata);
 tcg_temp_free_i32(cpu_index);
 }
 
+static void gen_empty_udata_cb_no_wg(void)
+{
+gen_empty_udata_cb(gen_helper_plugin_vcpu_udata_cb_no_wg);
+}
+
+static void gen_empty_udata_cb_no_rwg(void)
+{
+gen_empty_udata_cb(gen_helper_plugin_vcpu_udata_cb_no_rwg);
+}
+
 /*
  * For now we only support addi_i64.
  * When we support more ops, we can generate one empty inline cb for each.
@@ -192,7 +206,8 @@ static void plugin_gen_empty_callback(enum plugin_gen_from 
from)
 gen_empty_mem_helper);
 /* fall through */
 case PLUGIN_GEN_FROM_TB:
-gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb);
+gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb_no_rwg);
+gen_wrapped(from, PLUGIN_GEN_CB_UDATA_R, gen_empty_udata_cb_no_wg);
 gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb);
 break;
 default:
@@ -588,6 +603,12 @@ static void plugin_gen_tb_udata(const struct 
qemu_plugin_tb *ptb,
 inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR], begin_op);
 }
 
+static void plugin_gen_tb_udata_r(const struct qemu_plugin_tb *ptb,
+  TCGOp *begin_op)
+{
+inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR_R], begin_op);
+}
+
 static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb,
  TCGOp *begin_op)
 {
@@ -602,6 +623,14 @@ static void plugin_gen_insn_udata(const struct 
qemu_plugin_tb *ptb,
 inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], begin_op);
 }
 
+static void plugin_gen_insn_udata_r(const struct qemu_plugin_tb *ptb,
+TCGOp *begin_op, int insn_idx)
+{
+struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
+
+inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR_R], begin_op);
+}
+
 static void plugin_gen_insn_inline(const struct qemu_plugin_tb *ptb,
TCGOp *begin_op, int 

[PATCH 09/23] gdbstub: Infer number of core registers from XML

2024-02-16 Thread Alex Bennée
From: Akihiko Odaki 

GDBFeature has the num_regs member so use it where applicable to
remove magic numbers.

Signed-off-by: Akihiko Odaki 
Message-Id: <20240103173349.398526-34-alex.ben...@linaro.org>
Message-Id: <20231213-gdb-v17-8-777047380...@daynix.com>
Signed-off-by: Alex Bennée 
---
 include/hw/core/cpu.h   | 3 ++-
 target/s390x/cpu.h  | 2 --
 gdbstub/gdbstub.c   | 5 -
 target/arm/cpu.c| 1 -
 target/arm/cpu64.c  | 1 -
 target/avr/cpu.c| 1 -
 target/hexagon/cpu.c| 1 -
 target/i386/cpu.c   | 2 --
 target/loongarch/cpu.c  | 2 --
 target/m68k/cpu.c   | 1 -
 target/microblaze/cpu.c | 1 -
 target/riscv/cpu.c  | 1 -
 target/rx/cpu.c | 1 -
 target/s390x/cpu.c  | 1 -
 14 files changed, 6 insertions(+), 17 deletions(-)

diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index 4385ce54c99..1bbf21b2201 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -126,7 +126,8 @@ struct SysemuCPUOps;
  * @gdb_adjust_breakpoint: Callback for adjusting the address of a
  *   breakpoint.  Used by AVR to handle a gdb mis-feature with
  *   its Harvard architecture split code and data.
- * @gdb_num_core_regs: Number of core registers accessible to GDB.
+ * @gdb_num_core_regs: Number of core registers accessible to GDB or 0 to infer
+ * from @gdb_core_xml_file.
  * @gdb_core_xml_file: File name for core registers GDB XML description.
  * @gdb_stop_before_watchpoint: Indicates whether GDB expects the CPU to stop
  *   before the insn which triggers a watchpoint rather than after it.
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index d37a49b4d92..43a46a5a068 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -491,8 +491,6 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState *env, 
vaddr *pc,
 #define S390_R13_REGNUM 15
 #define S390_R14_REGNUM 16
 #define S390_R15_REGNUM 17
-/* Total Core Registers. */
-#define S390_NUM_CORE_REGS 18
 
 static inline void setcc(S390CPU *cpu, uint64_t cc)
 {
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index d573f808d2e..f766ee277a0 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -546,9 +546,12 @@ void gdb_init_cpu(CPUState *cpu)
 gdb_register_feature(cpu, 0,
  cc->gdb_read_register, cc->gdb_write_register,
  feature);
+cpu->gdb_num_regs = cpu->gdb_num_g_regs = feature->num_regs;
 }
 
-cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
+if (cc->gdb_num_core_regs) {
+cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
+}
 }
 
 void gdb_register_coprocessor(CPUState *cpu,
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 5fa86bc8d55..84887084d95 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -2515,7 +2515,6 @@ static void arm_cpu_class_init(ObjectClass *oc, void 
*data)
 #ifndef CONFIG_USER_ONLY
 cc->sysemu_ops = _sysemu_ops;
 #endif
-cc->gdb_num_core_regs = 26;
 cc->gdb_arch_name = arm_gdb_arch_name;
 cc->gdb_get_dynamic_xml = arm_gdb_get_dynamic_xml;
 cc->gdb_stop_before_watchpoint = true;
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 8e30a7993ea..869d8dd24ee 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -793,7 +793,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void 
*data)
 
 cc->gdb_read_register = aarch64_cpu_gdb_read_register;
 cc->gdb_write_register = aarch64_cpu_gdb_write_register;
-cc->gdb_num_core_regs = 34;
 cc->gdb_core_xml_file = "aarch64-core.xml";
 cc->gdb_arch_name = aarch64_gdb_arch_name;
 
diff --git a/target/avr/cpu.c b/target/avr/cpu.c
index a40f445af21..a50170bc69a 100644
--- a/target/avr/cpu.c
+++ b/target/avr/cpu.c
@@ -251,7 +251,6 @@ static void avr_cpu_class_init(ObjectClass *oc, void *data)
 cc->gdb_read_register = avr_cpu_gdb_read_register;
 cc->gdb_write_register = avr_cpu_gdb_write_register;
 cc->gdb_adjust_breakpoint = avr_cpu_gdb_adjust_breakpoint;
-cc->gdb_num_core_regs = 35;
 cc->gdb_core_xml_file = "avr-cpu.xml";
 cc->tcg_ops = _tcg_ops;
 }
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index ebe804e2931..a10d87b8220 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -362,7 +362,6 @@ static void hexagon_cpu_class_init(ObjectClass *c, void 
*data)
 cc->get_pc = hexagon_cpu_get_pc;
 cc->gdb_read_register = hexagon_gdb_read_register;
 cc->gdb_write_register = hexagon_gdb_write_register;
-cc->gdb_num_core_regs = TOTAL_PER_THREAD_REGS;
 cc->gdb_stop_before_watchpoint = true;
 cc->gdb_core_xml_file = "hexagon-core.xml";
 cc->disas_set_info = hexagon_cpu_disas_set_info;
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 0cd32a6fce3..71c14a6d3c8 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -7979,10 +7979,8 @@ static void x86_cpu_common_class_init(ObjectClass *oc, 
void *data)
 cc->gdb_arch_name = 

[PATCH 12/23] plugins: remove previous n_vcpus functions from API

2024-02-16 Thread Alex Bennée
From: Pierrick Bouvier 

This information is already accessible using qemu_info_t during plugin
install.

We will introduce another function (qemu_plugin_num_vcpus) which
represent how many cpus were enabled, by tracking new cpu indexes.

It's a breaking change, so we bump API version.

Signed-off-by: Pierrick Bouvier 
Reviewed-by: Richard Henderson 
Message-Id: <20240213094009.150349-2-pierrick.bouv...@linaro.org>
Signed-off-by: Alex Bennée 
---
 include/qemu/qemu-plugin.h   | 10 +++---
 plugins/plugin.h |  2 +-
 contrib/plugins/cache.c  |  2 +-
 plugins/api.c| 30 --
 plugins/qemu-plugins.symbols |  2 --
 5 files changed, 5 insertions(+), 41 deletions(-)

diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
index 4daab6efd29..e45181c793c 100644
--- a/include/qemu/qemu-plugin.h
+++ b/include/qemu/qemu-plugin.h
@@ -50,11 +50,13 @@ typedef uint64_t qemu_plugin_id_t;
  *
  * The plugins export the API they were built against by exposing the
  * symbol qemu_plugin_version which can be checked.
+ *
+ * version 2: removed qemu_plugin_n_vcpus and qemu_plugin_n_max_vcpus
  */
 
 extern QEMU_PLUGIN_EXPORT int qemu_plugin_version;
 
-#define QEMU_PLUGIN_VERSION 1
+#define QEMU_PLUGIN_VERSION 2
 
 /**
  * struct qemu_info_t - system information for plugins
@@ -643,12 +645,6 @@ QEMU_PLUGIN_API
 void qemu_plugin_register_atexit_cb(qemu_plugin_id_t id,
 qemu_plugin_udata_cb_t cb, void *userdata);
 
-/* returns -1 in user-mode */
-int qemu_plugin_n_vcpus(void);
-
-/* returns -1 in user-mode */
-int qemu_plugin_n_max_vcpus(void);
-
 /**
  * qemu_plugin_outs() - output string via QEMU's logging system
  * @string: a string
diff --git a/plugins/plugin.h b/plugins/plugin.h
index 5eb2fdbc85e..90f3f324ab6 100644
--- a/plugins/plugin.h
+++ b/plugins/plugin.h
@@ -15,7 +15,7 @@
 #include 
 #include "qemu/qht.h"
 
-#define QEMU_PLUGIN_MIN_VERSION 0
+#define QEMU_PLUGIN_MIN_VERSION 2
 
 /* global state */
 struct qemu_plugin_state {
diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c
index 9e7ade3b374..c5c8ac75a9c 100644
--- a/contrib/plugins/cache.c
+++ b/contrib/plugins/cache.c
@@ -767,7 +767,7 @@ int qemu_plugin_install(qemu_plugin_id_t id, const 
qemu_info_t *info,
 
 policy = LRU;
 
-cores = sys ? qemu_plugin_n_vcpus() : 1;
+cores = sys ? info->system.smp_vcpus : 1;
 
 for (i = 0; i < argc; i++) {
 char *opt = argv[i];
diff --git a/plugins/api.c b/plugins/api.c
index 5521b0ad36c..2926b1961a8 100644
--- a/plugins/api.c
+++ b/plugins/api.c
@@ -342,36 +342,6 @@ const char *qemu_plugin_hwaddr_device_name(const struct 
qemu_plugin_hwaddr *h)
 #endif
 }
 
-/*
- * Queries to the number and potential maximum number of vCPUs there
- * will be. This helps the plugin dimension per-vcpu arrays.
- */
-
-#ifndef CONFIG_USER_ONLY
-static MachineState * get_ms(void)
-{
-return MACHINE(qdev_get_machine());
-}
-#endif
-
-int qemu_plugin_n_vcpus(void)
-{
-#ifdef CONFIG_USER_ONLY
-return -1;
-#else
-return get_ms()->smp.cpus;
-#endif
-}
-
-int qemu_plugin_n_max_vcpus(void)
-{
-#ifdef CONFIG_USER_ONLY
-return -1;
-#else
-return get_ms()->smp.max_cpus;
-#endif
-}
-
 /*
  * Plugin output
  */
diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
index 71f6c90549d..ca806000d54 100644
--- a/plugins/qemu-plugins.symbols
+++ b/plugins/qemu-plugins.symbols
@@ -16,8 +16,6 @@
   qemu_plugin_mem_is_sign_extended;
   qemu_plugin_mem_is_store;
   qemu_plugin_mem_size_shift;
-  qemu_plugin_n_max_vcpus;
-  qemu_plugin_n_vcpus;
   qemu_plugin_outs;
   qemu_plugin_path_to_binary;
   qemu_plugin_register_atexit_cb;
-- 
2.39.2




[PATCH 11/23] gdbstub: Add members to identify registers to GDBFeature

2024-02-16 Thread Alex Bennée
From: Akihiko Odaki 

These members will be used to help plugins to identify registers.
The added members in instances of GDBFeature dynamically generated by
CPUs will be filled in later changes.

Signed-off-by: Akihiko Odaki 
Message-Id: <20240103173349.398526-36-alex.ben...@linaro.org>
Message-Id: <20231213-gdb-v17-10-777047380...@daynix.com>
Signed-off-by: Alex Bennée 
---
 include/exec/gdbstub.h  |  3 +++
 gdbstub/gdbstub.c   | 12 +---
 target/riscv/gdbstub.c  |  4 +---
 scripts/feature_to_c.py | 14 +-
 4 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index 82a8afa237f..da9ddfe54c5 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -13,12 +13,15 @@
 typedef struct GDBFeature {
 const char *xmlname;
 const char *xml;
+const char *name;
+const char * const *regs;
 int num_regs;
 } GDBFeature;
 
 typedef struct GDBFeatureBuilder {
 GDBFeature *feature;
 GPtrArray *xml;
+GPtrArray *regs;
 int base_reg;
 } GDBFeatureBuilder;
 
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index f766ee277a0..a55b5e6581a 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -419,9 +419,10 @@ void gdb_feature_builder_init(GDBFeatureBuilder *builder, 
GDBFeature *feature,
 builder->feature = feature;
 builder->xml = g_ptr_array_new();
 g_ptr_array_add(builder->xml, header);
+builder->regs = g_ptr_array_new();
 builder->base_reg = base_reg;
 feature->xmlname = xmlname;
-feature->num_regs = 0;
+feature->name = name;
 }
 
 void gdb_feature_builder_append_tag(const GDBFeatureBuilder *builder,
@@ -440,10 +441,12 @@ void gdb_feature_builder_append_reg(const 
GDBFeatureBuilder *builder,
 const char *type,
 const char *group)
 {
-if (builder->feature->num_regs < regnum) {
-builder->feature->num_regs = regnum;
+if (builder->regs->len <= regnum) {
+g_ptr_array_set_size(builder->regs, regnum + 1);
 }
 
+builder->regs->pdata[regnum] = (gpointer *)name;
+
 if (group) {
 gdb_feature_builder_append_tag(
 builder,
@@ -469,6 +472,9 @@ void gdb_feature_builder_end(const GDBFeatureBuilder 
*builder)
 }
 
 g_ptr_array_free(builder->xml, TRUE);
+
+builder->feature->num_regs = builder->regs->len;
+builder->feature->regs = (void *)g_ptr_array_free(builder->regs, FALSE);
 }
 
 const GDBFeature *gdb_find_static_feature(const char *xmlname)
diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
index 546e8692d17..be7a02cd903 100644
--- a/target/riscv/gdbstub.c
+++ b/target/riscv/gdbstub.c
@@ -266,11 +266,9 @@ static GDBFeature *riscv_gen_dynamic_csr_feature(CPUState 
*cs, int base_reg)
 }
 predicate = csr_ops[i].predicate;
 if (predicate && (predicate(env, i) == RISCV_EXCP_NONE)) {
-g_autofree char *dynamic_name = NULL;
 name = csr_ops[i].name;
 if (!name) {
-dynamic_name = g_strdup_printf("csr%03x", i);
-name = dynamic_name;
+name = g_strdup_printf("csr%03x", i);
 }
 
 gdb_feature_builder_append_reg(, name, bitsize, i,
diff --git a/scripts/feature_to_c.py b/scripts/feature_to_c.py
index e04d6b2df7f..807af0e685c 100644
--- a/scripts/feature_to_c.py
+++ b/scripts/feature_to_c.py
@@ -50,7 +50,9 @@ def writeliteral(indent, bytes):
 sys.stderr.write(f'unexpected start tag: {element.tag}\n')
 exit(1)
 
+feature_name = element.attrib['name']
 regnum = 0
+regnames = []
 regnums = []
 tags = ['feature']
 for event, element in events:
@@ -67,6 +69,7 @@ def writeliteral(indent, bytes):
 if 'regnum' in element.attrib:
 regnum = int(element.attrib['regnum'])
 
+regnames.append(element.attrib['name'])
 regnums.append(regnum)
 regnum += 1
 
@@ -85,6 +88,15 @@ def writeliteral(indent, bytes):
 writeliteral(8, bytes(os.path.basename(input), 'utf-8'))
 sys.stdout.write(',\n')
 writeliteral(8, read)
-sys.stdout.write(f',\n{num_regs},\n}},\n')
+sys.stdout.write(',\n')
+writeliteral(8, bytes(feature_name, 'utf-8'))
+sys.stdout.write(',\n(const char * const []) {\n')
+
+for index, regname in enumerate(regnames):
+sys.stdout.write(f'[{regnums[index] - base_reg}] =\n')
+writeliteral(16, bytes(regname, 'utf-8'))
+sys.stdout.write(',\n')
+
+sys.stdout.write(f'}},\n{num_regs},\n}},\n')
 
 sys.stdout.write('{ NULL }\n};\n')
-- 
2.39.2




[PATCH 03/23] target/ppc: Use GDBFeature for dynamic XML

2024-02-16 Thread Alex Bennée
From: Akihiko Odaki 

In preparation for a change to use GDBFeature as a parameter of
gdb_register_coprocessor(), convert the internal representation of
dynamic feature from plain XML to GDBFeature.

Signed-off-by: Akihiko Odaki 
Reviewed-by: Richard Henderson 
Message-Id: <20240103173349.398526-28-alex.ben...@linaro.org>
Message-Id: <20231213-gdb-v17-2-777047380...@daynix.com>
Signed-off-by: Alex Bennée 
---
 target/ppc/cpu-qom.h  |  1 +
 target/ppc/cpu.h  |  4 +---
 target/ppc/cpu_init.c |  4 
 target/ppc/gdbstub.c  | 51 ---
 4 files changed, 21 insertions(+), 39 deletions(-)

diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h
index 0241609efef..8247fa23367 100644
--- a/target/ppc/cpu-qom.h
+++ b/target/ppc/cpu-qom.h
@@ -20,6 +20,7 @@
 #ifndef QEMU_PPC_CPU_QOM_H
 #define QEMU_PPC_CPU_QOM_H
 
+#include "exec/gdbstub.h"
 #include "hw/core/cpu.h"
 
 #ifdef TARGET_PPC64
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index a44de22ca4a..200652e2c87 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1471,8 +1471,7 @@ struct PowerPCCPUClass {
 int bfd_mach;
 uint32_t l1_dcache_size, l1_icache_size;
 #ifndef CONFIG_USER_ONLY
-unsigned int gdb_num_sprs;
-const char *gdb_spr_xml;
+GDBFeature gdb_spr;
 #endif
 const PPCHash64Options *hash64_opts;
 struct ppc_radix_page_info *radix_page_info;
@@ -1525,7 +1524,6 @@ int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t 
*buf, int reg);
 int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg);
 #ifndef CONFIG_USER_ONLY
 hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
-void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu);
 const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name);
 #endif
 int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 9931372a08a..4f6903d3f16 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -6682,10 +6682,6 @@ static void init_ppc_proc(PowerPCCPU *cpu)
 /* PowerPC implementation specific initialisations (SPRs, timers, ...) */
 (*pcc->init_proc)(env);
 
-#if !defined(CONFIG_USER_ONLY)
-ppc_gdb_gen_spr_xml(cpu);
-#endif
-
 /* MSR bits & flags consistency checks */
 if (env->msr_mask & (1 << 25)) {
 switch (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE)) {
diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c
index ec5731e5d67..e3be3dbd109 100644
--- a/target/ppc/gdbstub.c
+++ b/target/ppc/gdbstub.c
@@ -300,15 +300,23 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, 
uint8_t *mem_buf, int n)
 }
 
 #ifndef CONFIG_USER_ONLY
-void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu)
+static void gdb_gen_spr_feature(CPUState *cs)
 {
-PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
+PowerPCCPU *cpu = POWERPC_CPU(cs);
 CPUPPCState *env = >env;
-GString *xml;
-char *spr_name;
+GDBFeatureBuilder builder;
 unsigned int num_regs = 0;
 int i;
 
+if (pcc->gdb_spr.xml) {
+return;
+}
+
+gdb_feature_builder_init(, >gdb_spr,
+ "org.qemu.power.spr", "power-spr.xml",
+ cs->gdb_num_regs);
+
 for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) {
 ppc_spr_t *spr = >spr_cb[i];
 
@@ -326,35 +334,13 @@ void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu)
  */
 spr->gdb_id = num_regs;
 num_regs++;
-}
-
-if (pcc->gdb_spr_xml) {
-return;
-}
 
-xml = g_string_new("");
-g_string_append(xml, "");
-g_string_append(xml, "");
-
-for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) {
-ppc_spr_t *spr = >spr_cb[i];
-
-if (!spr->name) {
-continue;
-}
-
-spr_name = g_ascii_strdown(spr->name, -1);
-g_string_append_printf(xml, "");
+gdb_feature_builder_append_reg(, g_ascii_strdown(spr->name, 
-1),
+   TARGET_LONG_BITS, num_regs,
+   "int", "spr");
 }
 
-g_string_append(xml, "");
-
-pcc->gdb_num_sprs = num_regs;
-pcc->gdb_spr_xml = g_string_free(xml, false);
+gdb_feature_builder_end();
 }
 
 const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name)
@@ -362,7 +348,7 @@ const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const 
char *xml_name)
 PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
 
 if (strcmp(xml_name, "power-spr.xml") == 0) {
-return pcc->gdb_spr_xml;
+return pcc->gdb_spr.xml;
 }
 return NULL;
 }
@@ -599,7 +585,8 @@ void ppc_gdb_init(CPUState *cs, PowerPCCPUClass *pcc)
  32, "power-vsx.xml", 0);
 }
 #ifndef CONFIG_USER_ONLY
+gdb_gen_spr_feature(cs);
 gdb_register_coprocessor(cs, gdb_get_spr_reg, gdb_set_spr_reg,
- pcc->gdb_num_sprs, "power-spr.xml", 0);
+

[PATCH 07/23] gdbstub: Change gdb_get_reg_cb and gdb_set_reg_cb

2024-02-16 Thread Alex Bennée
From: Akihiko Odaki 

Align the parameters of gdb_get_reg_cb and gdb_set_reg_cb with the
gdb_read_register and gdb_write_register members of CPUClass to allow
to unify the logic to access registers of the core and coprocessors
in the future.

Signed-off-by: Akihiko Odaki 
Reviewed-by: Alex Bennée 
Message-Id: <20240103173349.398526-32-alex.ben...@linaro.org>
Message-Id: <20231213-gdb-v17-6-777047380...@daynix.com>
Signed-off-by: Alex Bennée 
---
 include/exec/gdbstub.h  |  4 +-
 target/arm/internals.h  | 12 +++---
 target/hexagon/internal.h   |  4 +-
 target/microblaze/cpu.h |  4 +-
 gdbstub/gdbstub.c   |  6 +--
 target/arm/gdbstub.c| 51 
 target/arm/gdbstub64.c  | 27 +
 target/hexagon/gdbstub.c| 10 -
 target/loongarch/gdbstub.c  | 11 --
 target/m68k/helper.c| 20 --
 target/microblaze/gdbstub.c |  9 -
 target/ppc/gdbstub.c| 46 +-
 target/riscv/gdbstub.c  | 50 +---
 target/s390x/gdbstub.c  | 77 -
 14 files changed, 238 insertions(+), 93 deletions(-)

diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index ac6fce99a64..bcaab1bc750 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -24,8 +24,8 @@ typedef struct GDBFeatureBuilder {
 
 
 /* Get or set a register.  Returns the size of the register.  */
-typedef int (*gdb_get_reg_cb)(CPUArchState *env, GByteArray *buf, int reg);
-typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t *buf, int reg);
+typedef int (*gdb_get_reg_cb)(CPUState *cpu, GByteArray *buf, int reg);
+typedef int (*gdb_set_reg_cb)(CPUState *cpu, uint8_t *buf, int reg);
 
 /**
  * gdb_register_coprocessor() - register a supplemental set of registers
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 05eb9daac7d..860bcc0c664 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -1452,12 +1452,12 @@ static inline uint64_t pmu_counter_mask(CPUARMState 
*env)
 
 #ifdef TARGET_AARCH64
 GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cpu, int base_reg);
-int aarch64_gdb_get_sve_reg(CPUARMState *env, GByteArray *buf, int reg);
-int aarch64_gdb_set_sve_reg(CPUARMState *env, uint8_t *buf, int reg);
-int aarch64_gdb_get_fpu_reg(CPUARMState *env, GByteArray *buf, int reg);
-int aarch64_gdb_set_fpu_reg(CPUARMState *env, uint8_t *buf, int reg);
-int aarch64_gdb_get_pauth_reg(CPUARMState *env, GByteArray *buf, int reg);
-int aarch64_gdb_set_pauth_reg(CPUARMState *env, uint8_t *buf, int reg);
+int aarch64_gdb_get_sve_reg(CPUState *cs, GByteArray *buf, int reg);
+int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg);
+int aarch64_gdb_get_fpu_reg(CPUState *cs, GByteArray *buf, int reg);
+int aarch64_gdb_set_fpu_reg(CPUState *cs, uint8_t *buf, int reg);
+int aarch64_gdb_get_pauth_reg(CPUState *cs, GByteArray *buf, int reg);
+int aarch64_gdb_set_pauth_reg(CPUState *cs, uint8_t *buf, int reg);
 void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp);
 void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp);
 void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp);
diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h
index d732b6bb3c7..beb08cb7e38 100644
--- a/target/hexagon/internal.h
+++ b/target/hexagon/internal.h
@@ -33,8 +33,8 @@
 
 int hexagon_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
 int hexagon_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
-int hexagon_hvx_gdb_read_register(CPUHexagonState *env, GByteArray *mem_buf, 
int n);
-int hexagon_hvx_gdb_write_register(CPUHexagonState *env, uint8_t *mem_buf, int 
n);
+int hexagon_hvx_gdb_read_register(CPUState *env, GByteArray *mem_buf, int n);
+int hexagon_hvx_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n);
 
 void hexagon_debug_vreg(CPUHexagonState *env, int regnum);
 void hexagon_debug_qreg(CPUHexagonState *env, int regnum);
diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h
index 446af5dd4ca..c0c7574dbd5 100644
--- a/target/microblaze/cpu.h
+++ b/target/microblaze/cpu.h
@@ -381,8 +381,8 @@ G_NORETURN void mb_cpu_do_unaligned_access(CPUState *cs, 
vaddr vaddr,
 void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
 int mb_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
 int mb_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
-int mb_cpu_gdb_read_stack_protect(CPUArchState *cpu, GByteArray *buf, int reg);
-int mb_cpu_gdb_write_stack_protect(CPUArchState *cpu, uint8_t *buf, int reg);
+int mb_cpu_gdb_read_stack_protect(CPUState *cs, GByteArray *buf, int reg);
+int mb_cpu_gdb_write_stack_protect(CPUState *cs, uint8_t *buf, int reg);
 
 static inline uint32_t mb_cpu_read_msr(const CPUMBState *env)
 {
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 0ea417b2c9a..486ceb52d2e 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -502,7 +502,6 @@ const GDBFeature *gdb_find_static_feature(const char 

[PATCH 08/23] gdbstub: Simplify XML lookup

2024-02-16 Thread Alex Bennée
From: Akihiko Odaki 

Now we know all instances of GDBFeature that is used in CPU so we can
traverse them to find XML. This removes the need for a CPU-specific
lookup function for dynamic XMLs.

Signed-off-by: Akihiko Odaki 
Reviewed-by: Alex Bennée 
Message-Id: <20240103173349.398526-33-alex.ben...@linaro.org>
Message-Id: <20231213-gdb-v17-7-777047380...@daynix.com>
Signed-off-by: Alex Bennée 
---
 include/exec/gdbstub.h |   6 +++
 gdbstub/gdbstub.c  | 118 +
 hw/core/cpu-common.c   |   5 +-
 3 files changed, 69 insertions(+), 60 deletions(-)

diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index bcaab1bc750..82a8afa237f 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -27,6 +27,12 @@ typedef struct GDBFeatureBuilder {
 typedef int (*gdb_get_reg_cb)(CPUState *cpu, GByteArray *buf, int reg);
 typedef int (*gdb_set_reg_cb)(CPUState *cpu, uint8_t *buf, int reg);
 
+/**
+ * gdb_init_cpu(): Initialize the CPU for gdbstub.
+ * @cpu: The CPU to be initialized.
+ */
+void gdb_init_cpu(CPUState *cpu);
+
 /**
  * gdb_register_coprocessor() - register a supplemental set of registers
  * @cpu - the CPU associated with registers
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 486ceb52d2e..d573f808d2e 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -352,6 +352,7 @@ static const char *get_feature_xml(const char *p, const 
char **newp,
 {
 CPUState *cpu = gdb_get_first_cpu_in_process(process);
 CPUClass *cc = CPU_GET_CLASS(cpu);
+GDBRegisterState *r;
 size_t len;
 
 /*
@@ -365,7 +366,6 @@ static const char *get_feature_xml(const char *p, const 
char **newp,
 /* Is it the main target xml? */
 if (strncmp(p, "target.xml", len) == 0) {
 if (!process->target_xml) {
-GDBRegisterState *r;
 g_autoptr(GPtrArray) xml = g_ptr_array_new_with_free_func(g_free);
 
 g_ptr_array_add(
@@ -380,18 +380,12 @@ static const char *get_feature_xml(const char *p, const 
char **newp,
 g_markup_printf_escaped("%s",
 cc->gdb_arch_name(cpu)));
 }
-g_ptr_array_add(
-xml,
-g_markup_printf_escaped("",
-cc->gdb_core_xml_file));
-if (cpu->gdb_regs) {
-for (guint i = 0; i < cpu->gdb_regs->len; i++) {
-r = _array_index(cpu->gdb_regs, GDBRegisterState, i);
-g_ptr_array_add(
-xml,
-g_markup_printf_escaped("",
-r->feature->xmlname));
-}
+for (guint i = 0; i < cpu->gdb_regs->len; i++) {
+r = _array_index(cpu->gdb_regs, GDBRegisterState, i);
+g_ptr_array_add(
+xml,
+g_markup_printf_escaped("",
+r->feature->xmlname));
 }
 g_ptr_array_add(xml, g_strdup(""));
 g_ptr_array_add(xml, NULL);
@@ -400,20 +394,11 @@ static const char *get_feature_xml(const char *p, const 
char **newp,
 }
 return process->target_xml;
 }
-/* Is it dynamically generated by the target? */
-if (cc->gdb_get_dynamic_xml) {
-g_autofree char *xmlname = g_strndup(p, len);
-const char *xml = cc->gdb_get_dynamic_xml(cpu, xmlname);
-if (xml) {
-return xml;
-}
-}
-/* Is it one of the encoded gdb-xml/ files? */
-for (int i = 0; gdb_static_features[i].xmlname; i++) {
-const char *name = gdb_static_features[i].xmlname;
-if ((strncmp(name, p, len) == 0) &&
-strlen(name) == len) {
-return gdb_static_features[i].xml;
+/* Is it one of the features? */
+for (guint i = 0; i < cpu->gdb_regs->len; i++) {
+r = _array_index(cpu->gdb_regs, GDBRegisterState, i);
+if (strncmp(p, r->feature->xmlname, len) == 0) {
+return r->feature->xml;
 }
 }
 
@@ -508,12 +493,10 @@ static int gdb_read_register(CPUState *cpu, GByteArray 
*buf, int reg)
 return cc->gdb_read_register(cpu, buf, reg);
 }
 
-if (cpu->gdb_regs) {
-for (guint i = 0; i < cpu->gdb_regs->len; i++) {
-r = _array_index(cpu->gdb_regs, GDBRegisterState, i);
-if (r->base_reg <= reg && reg < r->base_reg + 
r->feature->num_regs) {
-return r->get_reg(cpu, buf, reg - r->base_reg);
-}
+for (guint i = 0; i < cpu->gdb_regs->len; i++) {
+r = _array_index(cpu->gdb_regs, GDBRegisterState, i);
+if (r->base_reg <= reg && reg < r->base_reg + r->feature->num_regs) {
+return r->get_reg(cpu, buf, reg - r->base_reg);
 }
 }
 return 0;
@@ -528,51 +511,70 @@ static int gdb_write_register(CPUState *cpu, uint8_t 
*mem_buf, int reg)
  

[PATCH 04/23] target/riscv: Use GDBFeature for dynamic XML

2024-02-16 Thread Alex Bennée
From: Akihiko Odaki 

In preparation for a change to use GDBFeature as a parameter of
gdb_register_coprocessor(), convert the internal representation of
dynamic feature from plain XML to GDBFeature.

Signed-off-by: Akihiko Odaki 
Message-Id: <20240103173349.398526-29-alex.ben...@linaro.org>
Message-Id: <20231213-gdb-v17-3-777047380...@daynix.com>
Signed-off-by: Alex Bennée 
---
 target/riscv/cpu.h |  5 +--
 target/riscv/cpu.c |  4 +--
 target/riscv/gdbstub.c | 81 +++---
 3 files changed, 41 insertions(+), 49 deletions(-)

diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index f52dce78baa..5d291a70925 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -24,6 +24,7 @@
 #include "hw/registerfields.h"
 #include "hw/qdev-properties.h"
 #include "exec/cpu-defs.h"
+#include "exec/gdbstub.h"
 #include "qemu/cpu-float.h"
 #include "qom/object.h"
 #include "qemu/int128.h"
@@ -445,8 +446,8 @@ struct ArchCPU {
 
 CPURISCVState env;
 
-char *dyn_csr_xml;
-char *dyn_vreg_xml;
+GDBFeature dyn_csr_feature;
+GDBFeature dyn_vreg_feature;
 
 /* Configuration Settings */
 RISCVCPUConfig cfg;
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 1b8d001d237..1b62e269b90 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -2305,9 +2305,9 @@ static const char *riscv_gdb_get_dynamic_xml(CPUState 
*cs, const char *xmlname)
 RISCVCPU *cpu = RISCV_CPU(cs);
 
 if (strcmp(xmlname, "riscv-csr.xml") == 0) {
-return cpu->dyn_csr_xml;
+return cpu->dyn_csr_feature.xml;
 } else if (strcmp(xmlname, "riscv-vector.xml") == 0) {
-return cpu->dyn_vreg_xml;
+return cpu->dyn_vreg_feature.xml;
 }
 
 return NULL;
diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
index ca9b71f7bbc..d8da84fa52e 100644
--- a/target/riscv/gdbstub.c
+++ b/target/riscv/gdbstub.c
@@ -214,14 +214,15 @@ static int riscv_gdb_set_virtual(CPURISCVState *cs, 
uint8_t *mem_buf, int n)
 return 0;
 }
 
-static int riscv_gen_dynamic_csr_xml(CPUState *cs, int base_reg)
+static GDBFeature *riscv_gen_dynamic_csr_feature(CPUState *cs, int base_reg)
 {
 RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cs);
 RISCVCPU *cpu = RISCV_CPU(cs);
 CPURISCVState *env = >env;
-GString *s = g_string_new(NULL);
+GDBFeatureBuilder builder;
 riscv_csr_predicate_fn predicate;
 int bitsize = riscv_cpu_max_xlen(mcc);
+const char *name;
 int i;
 
 #if !defined(CONFIG_USER_ONLY)
@@ -233,9 +234,9 @@ static int riscv_gen_dynamic_csr_xml(CPUState *cs, int 
base_reg)
 bitsize = 64;
 }
 
-g_string_printf(s, "");
-g_string_append_printf(s, "");
-g_string_append_printf(s, "");
+gdb_feature_builder_init(, >dyn_csr_feature,
+ "org.gnu.gdb.riscv.csr", "riscv-csr.xml",
+ base_reg);
 
 for (i = 0; i < CSR_TABLE_SIZE; i++) {
 if (env->priv_ver < csr_ops[i].min_priv_ver) {
@@ -243,72 +244,64 @@ static int riscv_gen_dynamic_csr_xml(CPUState *cs, int 
base_reg)
 }
 predicate = csr_ops[i].predicate;
 if (predicate && (predicate(env, i) == RISCV_EXCP_NONE)) {
-if (csr_ops[i].name) {
-g_string_append_printf(s, "", base_reg + i);
+
+gdb_feature_builder_append_reg(, name, bitsize, i,
+   "int", NULL);
 }
 }
 
-g_string_append_printf(s, "");
-
-cpu->dyn_csr_xml = g_string_free(s, false);
+gdb_feature_builder_end();
 
 #if !defined(CONFIG_USER_ONLY)
 env->debugger = false;
 #endif
 
-return CSR_TABLE_SIZE;
+return >dyn_csr_feature;
 }
 
-static int ricsv_gen_dynamic_vector_xml(CPUState *cs, int base_reg)
+static GDBFeature *ricsv_gen_dynamic_vector_feature(CPUState *cs, int base_reg)
 {
 RISCVCPU *cpu = RISCV_CPU(cs);
-GString *s = g_string_new(NULL);
-g_autoptr(GString) ts = g_string_new("");
-int reg_width = cpu->cfg.vlenb << 3;
-int num_regs = 0;
+int reg_width = cpu->cfg.vlenb;
+GDBFeatureBuilder builder;
 int i;
 
-g_string_printf(s, "");
-g_string_append_printf(s, "");
-g_string_append_printf(s, "");
+gdb_feature_builder_init(, >dyn_vreg_feature,
+ "org.gnu.gdb.riscv.vector", "riscv-vector.xml",
+ base_reg);
 
 /* First define types and totals in a whole VL */
 for (i = 0; i < ARRAY_SIZE(vec_lanes); i++) {
 int count = reg_width / vec_lanes[i].size;
-g_string_printf(ts, "%s", vec_lanes[i].id);
-g_string_append_printf(s,
-   "",
-   ts->str, vec_lanes[i].gdb_type, count);
+gdb_feature_builder_append_tag(
+, "",
+vec_lanes[i].id, vec_lanes[i].gdb_type, count);
 }
 
 /* Define unions */
-g_string_append_printf(s, "");
+gdb_feature_builder_append_tag(, "");
 for (i = 

[PATCH 10/23] hw/core/cpu: Remove gdb_get_dynamic_xml member

2024-02-16 Thread Alex Bennée
From: Akihiko Odaki 

This function is no longer used.

Signed-off-by: Akihiko Odaki 
Reviewed-by: Alex Bennée 
Message-Id: <20240103173349.398526-35-alex.ben...@linaro.org>
Message-Id: <20231213-gdb-v17-9-777047380...@daynix.com>
Signed-off-by: Alex Bennée 
---
 include/hw/core/cpu.h |  4 
 target/arm/cpu.h  |  6 --
 target/ppc/cpu.h  |  1 -
 target/arm/cpu.c  |  1 -
 target/arm/gdbstub.c  | 18 --
 target/ppc/cpu_init.c |  3 ---
 target/ppc/gdbstub.c  | 10 --
 target/riscv/cpu.c| 14 --
 8 files changed, 57 deletions(-)

diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index 1bbf21b2201..4b659799b00 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -133,9 +133,6 @@ struct SysemuCPUOps;
  *   before the insn which triggers a watchpoint rather than after it.
  * @gdb_arch_name: Optional callback that returns the architecture name known
  * to GDB. The caller must free the returned string with g_free.
- * @gdb_get_dynamic_xml: Callback to return dynamically generated XML for the
- *   gdb stub. Returns a pointer to the XML contents for the specified XML file
- *   or NULL if the CPU doesn't have a dynamically generated content for it.
  * @disas_set_info: Setup architecture specific components of disassembly info
  * @adjust_watchpoint_address: Perform a target-specific adjustment to an
  * address before attempting to match it against watchpoints.
@@ -167,7 +164,6 @@ struct CPUClass {
 
 const char *gdb_core_xml_file;
 const gchar * (*gdb_arch_name)(CPUState *cpu);
-const char * (*gdb_get_dynamic_xml)(CPUState *cpu, const char *xmlname);
 
 void (*disas_set_info)(CPUState *cpu, disassemble_info *info);
 
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 508a9c1e0d6..a5b3d8f7da7 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -1159,12 +1159,6 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, 
vaddr addr,
 int arm_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
 int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
 
-/* Returns the dynamically generated XML for the gdb stub.
- * Returns a pointer to the XML contents for the specified XML file or NULL
- * if the XML name doesn't match the predefined one.
- */
-const char *arm_gdb_get_dynamic_xml(CPUState *cpu, const char *xmlname);
-
 int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
  int cpuid, DumpState *s);
 int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs,
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 200652e2c87..a15e7c38faf 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -1524,7 +1524,6 @@ int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t 
*buf, int reg);
 int ppc_cpu_gdb_write_register_apple(CPUState *cpu, uint8_t *buf, int reg);
 #ifndef CONFIG_USER_ONLY
 hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
-const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name);
 #endif
 int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
int cpuid, DumpState *s);
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 84887084d95..b2ea5d65132 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -2516,7 +2516,6 @@ static void arm_cpu_class_init(ObjectClass *oc, void 
*data)
 cc->sysemu_ops = _sysemu_ops;
 #endif
 cc->gdb_arch_name = arm_gdb_arch_name;
-cc->gdb_get_dynamic_xml = arm_gdb_get_dynamic_xml;
 cc->gdb_stop_before_watchpoint = true;
 cc->disas_set_info = arm_disas_set_info;
 
diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c
index 059d84f98e5..a3bb73cfa7c 100644
--- a/target/arm/gdbstub.c
+++ b/target/arm/gdbstub.c
@@ -474,24 +474,6 @@ static GDBFeature 
*arm_gen_dynamic_m_secextreg_feature(CPUState *cs,
 #endif
 #endif /* CONFIG_TCG */
 
-const char *arm_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname)
-{
-ARMCPU *cpu = ARM_CPU(cs);
-
-if (strcmp(xmlname, "system-registers.xml") == 0) {
-return cpu->dyn_sysreg_feature.desc.xml;
-} else if (strcmp(xmlname, "sve-registers.xml") == 0) {
-return cpu->dyn_svereg_feature.desc.xml;
-} else if (strcmp(xmlname, "arm-m-system.xml") == 0) {
-return cpu->dyn_m_systemreg_feature.desc.xml;
-#ifndef CONFIG_USER_ONLY
-} else if (strcmp(xmlname, "arm-m-secext.xml") == 0) {
-return cpu->dyn_m_secextreg_feature.desc.xml;
-#endif
-}
-return NULL;
-}
-
 void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
 {
 CPUState *cs = CPU(cpu);
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 4f6903d3f16..9aae02115bf 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -7385,9 +7385,6 @@ static void ppc_cpu_class_init(ObjectClass *oc, void 
*data)
 #endif
 
 cc->gdb_num_core_regs = 71;
-#ifndef CONFIG_USER_ONLY
-cc->gdb_get_dynamic_xml = ppc_gdb_get_dynamic_xml;
-#endif
 #ifdef 

[PATCH 06/23] gdbstub: Use GDBFeature for GDBRegisterState

2024-02-16 Thread Alex Bennée
From: Akihiko Odaki 

Simplify GDBRegisterState by replacing num_regs and xml members with
one member that points to GDBFeature.

Signed-off-by: Akihiko Odaki 
Reviewed-by: Alex Bennée 
Reviewed-by: Philippe Mathieu-Daudé 
Message-Id: <20240103173349.398526-31-alex.ben...@linaro.org>
Message-Id: <20231213-gdb-v17-5-777047380...@daynix.com>
Signed-off-by: Alex Bennée 
---
 gdbstub/gdbstub.c | 14 ++
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 256599c8dfb..0ea417b2c9a 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -47,10 +47,9 @@
 
 typedef struct GDBRegisterState {
 int base_reg;
-int num_regs;
 gdb_get_reg_cb get_reg;
 gdb_set_reg_cb set_reg;
-const char *xml;
+const GDBFeature *feature;
 } GDBRegisterState;
 
 GDBState gdbserver_state;
@@ -391,7 +390,7 @@ static const char *get_feature_xml(const char *p, const 
char **newp,
 g_ptr_array_add(
 xml,
 g_markup_printf_escaped("",
-r->xml));
+r->feature->xmlname));
 }
 }
 g_ptr_array_add(xml, g_strdup(""));
@@ -513,7 +512,7 @@ static int gdb_read_register(CPUState *cpu, GByteArray 
*buf, int reg)
 if (cpu->gdb_regs) {
 for (guint i = 0; i < cpu->gdb_regs->len; i++) {
 r = _array_index(cpu->gdb_regs, GDBRegisterState, i);
-if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) {
+if (r->base_reg <= reg && reg < r->base_reg + 
r->feature->num_regs) {
 return r->get_reg(env, buf, reg - r->base_reg);
 }
 }
@@ -534,7 +533,7 @@ static int gdb_write_register(CPUState *cpu, uint8_t 
*mem_buf, int reg)
 if (cpu->gdb_regs) {
 for (guint i = 0; i < cpu->gdb_regs->len; i++) {
 r =  _array_index(cpu->gdb_regs, GDBRegisterState, i);
-if (r->base_reg <= reg && reg < r->base_reg + r->num_regs) {
+if (r->base_reg <= reg && reg < r->base_reg + 
r->feature->num_regs) {
 return r->set_reg(env, mem_buf, reg - r->base_reg);
 }
 }
@@ -553,7 +552,7 @@ void gdb_register_coprocessor(CPUState *cpu,
 for (i = 0; i < cpu->gdb_regs->len; i++) {
 /* Check for duplicates.  */
 s = _array_index(cpu->gdb_regs, GDBRegisterState, i);
-if (strcmp(s->xml, feature->xmlname) == 0) {
+if (s->feature == feature) {
 return;
 }
 }
@@ -565,10 +564,9 @@ void gdb_register_coprocessor(CPUState *cpu,
 g_array_set_size(cpu->gdb_regs, i + 1);
 s = _array_index(cpu->gdb_regs, GDBRegisterState, i);
 s->base_reg = cpu->gdb_num_regs;
-s->num_regs = feature->num_regs;
 s->get_reg = get_reg;
 s->set_reg = set_reg;
-s->xml = feature->xml;
+s->feature = feature;
 
 /* Add to end of list.  */
 cpu->gdb_num_regs += feature->num_regs;
-- 
2.39.2




[PATCH 05/23] gdbstub: Use GDBFeature for gdb_register_coprocessor

2024-02-16 Thread Alex Bennée
From: Akihiko Odaki 

This is a tree-wide change to introduce GDBFeature parameter to
gdb_register_coprocessor(). The new parameter just replaces num_regs
and xml parameters for now. GDBFeature will be utilized to simplify XML
lookup in a following change.

Signed-off-by: Akihiko Odaki 
Acked-by: Alex Bennée 
Message-Id: <20240103173349.398526-30-alex.ben...@linaro.org>
Message-Id: <20231213-gdb-v17-4-777047380...@daynix.com>
Signed-off-by: Alex Bennée 
---
 include/exec/gdbstub.h |  2 +-
 gdbstub/gdbstub.c  | 13 +++--
 target/arm/gdbstub.c   | 35 +++
 target/hexagon/cpu.c   |  3 +--
 target/loongarch/gdbstub.c |  2 +-
 target/m68k/helper.c   |  6 +++---
 target/microblaze/cpu.c|  5 +++--
 target/ppc/gdbstub.c   | 11 ++-
 target/riscv/gdbstub.c | 20 
 target/s390x/gdbstub.c | 28 +++-
 10 files changed, 60 insertions(+), 65 deletions(-)

diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h
index d8a3c56fa2b..ac6fce99a64 100644
--- a/include/exec/gdbstub.h
+++ b/include/exec/gdbstub.h
@@ -38,7 +38,7 @@ typedef int (*gdb_set_reg_cb)(CPUArchState *env, uint8_t 
*buf, int reg);
  */
 void gdb_register_coprocessor(CPUState *cpu,
   gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
-  int num_regs, const char *xml, int g_pos);
+  const GDBFeature *feature, int g_pos);
 
 /**
  * gdbserver_start: start the gdb server
diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c
index 7e73e916bdc..256599c8dfb 100644
--- a/gdbstub/gdbstub.c
+++ b/gdbstub/gdbstub.c
@@ -544,7 +544,7 @@ static int gdb_write_register(CPUState *cpu, uint8_t 
*mem_buf, int reg)
 
 void gdb_register_coprocessor(CPUState *cpu,
   gdb_get_reg_cb get_reg, gdb_set_reg_cb set_reg,
-  int num_regs, const char *xml, int g_pos)
+  const GDBFeature *feature, int g_pos)
 {
 GDBRegisterState *s;
 guint i;
@@ -553,7 +553,7 @@ void gdb_register_coprocessor(CPUState *cpu,
 for (i = 0; i < cpu->gdb_regs->len; i++) {
 /* Check for duplicates.  */
 s = _array_index(cpu->gdb_regs, GDBRegisterState, i);
-if (strcmp(s->xml, xml) == 0) {
+if (strcmp(s->xml, feature->xmlname) == 0) {
 return;
 }
 }
@@ -565,17 +565,18 @@ void gdb_register_coprocessor(CPUState *cpu,
 g_array_set_size(cpu->gdb_regs, i + 1);
 s = _array_index(cpu->gdb_regs, GDBRegisterState, i);
 s->base_reg = cpu->gdb_num_regs;
-s->num_regs = num_regs;
+s->num_regs = feature->num_regs;
 s->get_reg = get_reg;
 s->set_reg = set_reg;
-s->xml = xml;
+s->xml = feature->xml;
 
 /* Add to end of list.  */
-cpu->gdb_num_regs += num_regs;
+cpu->gdb_num_regs += feature->num_regs;
 if (g_pos) {
 if (g_pos != s->base_reg) {
 error_report("Error: Bad gdb register numbering for '%s', "
- "expected %d got %d", xml, g_pos, s->base_reg);
+ "expected %d got %d", feature->xml,
+ g_pos, s->base_reg);
 } else {
 cpu->gdb_num_g_regs = cpu->gdb_num_regs;
 }
diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c
index 5949adfb31a..f2b201d3125 100644
--- a/target/arm/gdbstub.c
+++ b/target/arm/gdbstub.c
@@ -483,14 +483,14 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
  */
 #ifdef TARGET_AARCH64
 if (isar_feature_aa64_sve(>isar)) {
-int nreg = arm_gen_dynamic_svereg_feature(cs, 
cs->gdb_num_regs)->num_regs;
+GDBFeature *feature = arm_gen_dynamic_svereg_feature(cs, 
cs->gdb_num_regs);
 gdb_register_coprocessor(cs, aarch64_gdb_get_sve_reg,
- aarch64_gdb_set_sve_reg, nreg,
- "sve-registers.xml", 0);
+ aarch64_gdb_set_sve_reg, feature, 0);
 } else {
 gdb_register_coprocessor(cs, aarch64_gdb_get_fpu_reg,
  aarch64_gdb_set_fpu_reg,
- 34, "aarch64-fpu.xml", 0);
+ 
gdb_find_static_feature("aarch64-fpu.xml"),
+ 0);
 }
 /*
  * Note that we report pauth information via the feature name
@@ -501,19 +501,22 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
 if (isar_feature_aa64_pauth(>isar)) {
 gdb_register_coprocessor(cs, aarch64_gdb_get_pauth_reg,
  aarch64_gdb_set_pauth_reg,
- 4, "aarch64-pauth.xml", 0);
+ 
gdb_find_static_feature("aarch64-pauth.xml"),
+   

[PATCH 01/23] tests/tcg: update licenses to GPLv2 as intended

2024-02-16 Thread Alex Bennée
My default header template is GPLv3 but for QEMU code we really should
stick to GPLv2-or-later (allowing others to up-license it if they
wish). While this is test code we should still be consistent on the
source distribution.

I wrote all of this code so its not a problem. However there remains
one GPLv3 file left which is the crt0-tc2x.S for TriCore.

Reviewed-by: Philippe Mathieu-Daudé 
Signed-off-by: Alex Bennée 
Message-Id: <20240215184036.214065-1-alex.ben...@linaro.org>
---
 tests/tcg/aarch64/semicall.h  | 4 ++--
 tests/tcg/arm/semicall.h  | 4 ++--
 tests/tcg/multiarch/float_helpers.h   | 4 ++--
 tests/tcg/riscv64/semicall.h  | 4 ++--
 tests/tcg/multiarch/arm-compat-semi/semiconsole.c | 4 ++--
 tests/tcg/multiarch/arm-compat-semi/semihosting.c | 4 ++--
 tests/tcg/multiarch/float_convd.c | 4 ++--
 tests/tcg/multiarch/float_convs.c | 4 ++--
 tests/tcg/multiarch/float_madds.c | 4 ++--
 tests/tcg/multiarch/libs/float_helpers.c  | 4 ++--
 tests/tcg/i386/system/boot.S  | 6 +++---
 tests/tcg/x86_64/system/boot.S| 6 +++---
 12 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/tests/tcg/aarch64/semicall.h b/tests/tcg/aarch64/semicall.h
index 8a3fce35c5f..30d4de9a549 100644
--- a/tests/tcg/aarch64/semicall.h
+++ b/tests/tcg/aarch64/semicall.h
@@ -1,10 +1,10 @@
 /*
  * Semihosting Tests - AArch64 helper
  *
- * Copyright (c) 2019
+ * Copyright (c) 2019, 2024
  * Written by Alex Bennée 
  *
- * SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 uintptr_t __semi_call(uintptr_t type, uintptr_t arg0)
diff --git a/tests/tcg/arm/semicall.h b/tests/tcg/arm/semicall.h
index ad8ac51310b..624937c5577 100644
--- a/tests/tcg/arm/semicall.h
+++ b/tests/tcg/arm/semicall.h
@@ -1,10 +1,10 @@
 /*
  * Semihosting Tests - ARM Helper
  *
- * Copyright (c) 2019
+ * Copyright (c) 2019, 2024
  * Written by Alex Bennée 
  *
- * SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 uintptr_t __semi_call(uintptr_t type, uintptr_t arg0)
diff --git a/tests/tcg/multiarch/float_helpers.h 
b/tests/tcg/multiarch/float_helpers.h
index 309f3f4bf10..c42ebe64b9e 100644
--- a/tests/tcg/multiarch/float_helpers.h
+++ b/tests/tcg/multiarch/float_helpers.h
@@ -1,9 +1,9 @@
 /*
  * Common Float Helpers
  *
- * Copyright (c) 2019 Linaro
+ * Copyright (c) 2019, 2024 Linaro
  *
- * SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include 
diff --git a/tests/tcg/riscv64/semicall.h b/tests/tcg/riscv64/semicall.h
index f8c88f32dc5..11d0650cb06 100644
--- a/tests/tcg/riscv64/semicall.h
+++ b/tests/tcg/riscv64/semicall.h
@@ -1,10 +1,10 @@
 /*
  * Semihosting Tests - RiscV64 Helper
  *
- * Copyright (c) 2021
+ * Copyright (c) 2021, 2024
  * Written by Alex Bennée 
  *
- * SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 uintptr_t __semi_call(uintptr_t type, uintptr_t arg0)
diff --git a/tests/tcg/multiarch/arm-compat-semi/semiconsole.c 
b/tests/tcg/multiarch/arm-compat-semi/semiconsole.c
index 1d82efc589d..1e2268f4b75 100644
--- a/tests/tcg/multiarch/arm-compat-semi/semiconsole.c
+++ b/tests/tcg/multiarch/arm-compat-semi/semiconsole.c
@@ -1,10 +1,10 @@
 /*
  * linux-user semihosting console
  *
- * Copyright (c) 2019
+ * Copyright (c) 2024
  * Written by Alex Bennée 
  *
- * SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #define SYS_READC   0x07
diff --git a/tests/tcg/multiarch/arm-compat-semi/semihosting.c 
b/tests/tcg/multiarch/arm-compat-semi/semihosting.c
index 8627eee3cf7..f609c01341a 100644
--- a/tests/tcg/multiarch/arm-compat-semi/semihosting.c
+++ b/tests/tcg/multiarch/arm-compat-semi/semihosting.c
@@ -1,10 +1,10 @@
 /*
  * linux-user semihosting checks
  *
- * Copyright (c) 2019
+ * Copyright (c) 2019, 2024
  * Written by Alex Bennée 
  *
- * SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #define SYS_WRITE0  0x04
diff --git a/tests/tcg/multiarch/float_convd.c 
b/tests/tcg/multiarch/float_convd.c
index 0a1f0f93dc5..58d7f8b4c58 100644
--- a/tests/tcg/multiarch/float_convd.c
+++ b/tests/tcg/multiarch/float_convd.c
@@ -1,9 +1,9 @@
 /*
  * Floating Point Convert Doubles to Various
  *
- * Copyright (c) 2019 Linaro
+ * Copyright (c) 2019, 2024 Linaro
  *
- * SPDX-License-Identifier: GPL-3.0-or-later
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include 
diff --git a/tests/tcg/multiarch/float_convs.c 
b/tests/tcg/multiarch/float_convs.c
index 2e4fa55324d..cb1fdd439e3 100644
--- a/tests/tcg/multiarch/float_convs.c
+++ b/tests/tcg/multiarch/float_convs.c
@@ -1,9 +1,9 @@
 /*
  * Floating Point Convert Single to Various
  *
- * Copyright (c) 2019 Linaro
+ * Copyright (c) 2019, 2024 Linaro
  

  1   2   3   >