On Thu, Jul 09, 2026 at 07:52:12PM +1000, Gavin Shan wrote:
> Hi Michael and Peter,
>
> On 6/26/26 10:07 AM, Gavin Shan wrote:
> > On 6/26/26 4:40 AM, Peter Maydell wrote:
> > > On Thu, 25 Jun 2026 at 17:47, Michael S. Tsirkin <[email protected]> wrote:
> > > >
> > > > On Thu, Jun 25, 2026 at 04:23:47PM +0100, Peter Maydell wrote:
> > > > > On Thu, 25 Jun 2026 at 15:52, Michael S. Tsirkin <[email protected]>
> > > > > wrote:
> > > > > > I think there is exactly 1 kinda reasonable case. A 2 byte
> > > > > > read/write at
> > > > > > offset 0x1 within a dword. This maps nicely to even classical PCI
> > > > > > byte
> > > > > > enable mechanism and so yes it works if your CPU can initiate these
> > > > > > things, and it's atomic.
> > > > > >
> > > > > > I tried reading LEDCTL on e1000e:
> > > > > >
> > > > > > byte @ 0xe00: 0x64
> > > > > > byte @ 0xe01: 0x2a
> > > > > > byte @ 0xe02: 0x00
> > > > > > byte @ 0xe03: 0x00
> > > > > > word @ 0xe00: 0x2a64
> > > > > > word @ 0xe01: 0x002a
> > > > > >
> > > > > > Works fine.
> > > > >
> > > > > The e1000e datasheet actually documents what it does in this
> > > > > case (slightly surprising, since hardware engineers love to
> > > > > leave this kind of corner case undocumented):
> > > > >
> > > > > # For registers that should be accessed as 32-bit double words,
> > > > > # partial writes (less than a 32-bit double word) does not take
> > > > > # effect (such as, the write is ignored).
> > > > > # Partial reads
> > > > > # return all 32 bits of data regardless of the byte enables.
> > > > > #
> > > > > # Note: Partial reads to clear-by-read registers (such as, ICR)
> > > > > # can have unexpected results since all 32 bits are actually read
> > > > > # regardless of the byte enables. Partial reads should not be done.
> > > > >
> > > > > So for this specific device that access is out-of-spec.
> > > >
> > > > You mean that access to clear by read should not be done, right?
> > >
> > > The datasheet is ambiguous about whether "Partial reads should
> > > not be done" is meant to apply generally or only to clear-by-read
> > > registers.
> > >
> >
> > I think the document intends to say: the partial reads shouldn't be issued
> > to
> > the clear-by-read registers. Those partial reads on no-read-side-affect
> > registers
> > would be fine.
> >
> > > > > I guess what I'm wondering is: can we just have code
> > > > > that does an aligned exact-width access in the 1/2/4/8
> > > > > byte aligned case, and the host's best approximation to
> > > > > an unaligned exact-width access for the 2/4/8 byte
> > > > > unaligned case?
> > > >
> > > > That's my idea, too.
> > > >
> >
> > I assume this the conclusion of our discussions? If so, we just need to have
> > unified function where __builtin_{memcpy, memmove}() are used for 1/2/4/8
> > bytes
> > access no matter if the address is aligned, and fall back to {memcpy,
> > memmove}()
> > for other cases?
> >
>
> Could you help to confirm if the conclusion is to have unified
> qemu_ram_{copy, move}()
> for all architectures? In qemu_ram_{copy, move}(), the fixed sized accesses
> like
> 1/2/4/8 bytes are handled by __builtin_{memcpy, memmove}() and fall back to
> memcpy/memmove() for other sized accesses. It's basically what was proposed by
> Michael :-)
No. __builtin_ and memcpy/memmove are same on modern linuxes.
I'd say as a 1st step, on x86 hosts we can do a fixed size
memmove for 1/2/4/8, on other hosts
*aligned* 1/2/4/8 bytes can do
__atomic_store ... __ATOMIC_RELAXED
and the rest can hobble along on memmove
and hope for the best.
And memcpy is likely not the right thing, ever.
> Something like below.
>
> -----> include/system/memory.h
>
> +/**
> + * qemu_ram_copy: copy data to ramblock
> + *
> + * @dst: destination where the data is copied to
> + * @src: source where the data is copied from
> + * @n: length of data to be copied
> + *
> + *
> + * Copy @n bytes from @src to @dst with the assumption that @src and @dst
> + * do not overlap. The fixed sized accesses like 1/2/4/8 bytes are handled
> + * by __builtin_memcpy() to avoid the optimizations applied to memcpy(),
> + * which can be unsafe to DMA/VCPU IO. Fall back to memcpy() for other
> + * sized accesses and the safety is ensured by users.
> + */
> +void qemu_ram_copy(void *dest, const void *src, size_t n);
> +
> +/**
> + * qemu_ram_move: move data to ramblock
> + *
> + * @dst: destination where the data is moved to
> + * @src: source where the data is moved from
> + * @n: length of data to be moved
> + *
> + * Move @n bytes from @src to @dst with the assumption that @src and @dst
> + * can overlap. The fixed sized accesses like 1/2/4/8 bytes are handled
> + * by __builtin_memmove() to avoid the optimizations applied to memmove(),
> + * which can be unsafe to DMA/VCPU IO. Fall back to memmove() for other
> + * sized accesses and the safety is ensured by users.
> + */
> +void qemu_ram_move(void *dest, const void *src, size_t n);
> +
>
> -----> system/physmem.c
>
> +void qemu_ram_copy(void *dst, const void *src, size_t n)
> +{
> + switch (n) {
> + case 1:
> + __builtin_memcpy(dst, src, 1);
> + break;
> + case 2:
> + __builtin_memcpy(dst, src, 2);
> + break;
> + case 4:
> + __builtin_memcpy(dst, src, 4);
> + break;
> + case 8:
> + __builtin_memcpy(dst, src, 8);
> + break;
> + default:
> + memcpy(dst, src, n);
> + }
> +}
> +
> +void qemu_ram_move(void *dst, const void *src, size_t n)
> +{
> + switch (n) {
> + case 1:
> + __builtin_memmove(dst, src, 1);
> + break;
> + case 2:
> + __builtin_memmove(dst, src, 2);
> + break;
> + case 4:
> + __builtin_memmove(dst, src, 4);
> + break;
> + case 8:
> + __builtin_memmove(dst, src, 8);
> + break;
> + default:
> + memmove(dst, src, n);
> + }
> +}
> +
>
>
> > > > > (so on sparc you get multiple smaller
> > > > > accesses, and on most archs including x86 and arm you
> > > > > get an unaligned load).
> > > >
> > > > I thought unaligned load from uncacheable on arm is
> > > > also a fault?
> > >
> > > For Arm the distinction is not cacheable/uncacheable
> > > but Normal vs Device. (Device is essentially for things
> > > which are not RAM; Normal is for RAM and RAM-like things,
> > > and includes all of Normal Non-cacheable, Normal WT-Cacheable
> > > and Normal WB-Cacheable.) Things mapped as Normal memory
> > > don't generate unaligned faults (unless the guest turned them
> > > on deliberately). For Device memory, it is IMPLEMENTATION
> > > DEFINED whether you get an alignment fault or not if you
> > > map something as Device that could have handled unaligned
> > > accesses if you had mapped it as Normal.
> > >
> > > > > That would mean the guest could
> > > > > potentially provoke a fault on the load/store on an
> > > > > access to a passthrough device, but if you give the
> > > > > guest passthrough access it can very likely provoke
> > > > > a fault anyway, depending on exactly what the device is.
> > > > >
> > > > > I think the most likely reason for an unaligned access
> > > > > in this codepath is "it's actually RAM, either really
> > > > > host RAM or else something memory-like in a BAR", and
> > > > > either way if the guest does a 4-byte unaligned access
> > > > > then doing a 4-byte unaligned access seems better than
> > > > > second-guessing it, even on non-x86.
> > >
> > > > Right though remember: whether it's RAM doesn't matter. What matters is
> > > > how we map it. qemu might fault because it maps NC but guest maps
> > > > cacheable and it's ok.
> > >
> > > If QEMU and the guest disagree about the memory attributes
> > > on Arm then we have already lost, because the architecture
> > > says that memory attribute mismatches result in a variety of
> > > undesirable effects including things like loss of cache coherency
> > > (i.e. read and writes via QEMU's NC mapping disagree with ones
> > > via the guest's cacheable mapping because the latter are hitting
> > > in the cache and the former are bypassing it).
> > >
> > > > But, all this in theory. At a high level, I personally think going with
> > > > what you propose as a 1st approximation is entirely reasonable, except
> > > > for one thing: we really should not crash qemu, since access can be from
> > > > guest userspace.
> > >
> > > You can't prevent faults entirely, though -- if the device
> > > being mapped has e.g. behaviour that says "unaligned accesses
> > > will fault" and then the x86 guest does an unaligned access,
> > > then the device will trigger a fault, and the fault is what
> > > you want because it's what the guest would see on real h/w.
> > > Unfortunately we don't have a convenient way to feed the
> > > fault back to the guest. At some level if you pass through
> > > host hardware you're relying on the guest to not do totally
> > > stupid things.
> > >
> > > thanks
> > > -- PMM
> > >
>
> Thanks,
> Gavin