On Fri, 22 Aug 2025 at 10:25, CJ Chen <cjc...@igel.co.jp> wrote: > > From: Tomoyuki Hirose <hrstmyk8...@gmail.com> > > The previous code ignored 'impl.unaligned' and handled unaligned > accesses as-is. But this implementation could not emulate specific > registers of some devices that allow unaligned access such as xHCI > Host Controller Capability Registers. > > This commit emulates an unaligned access with multiple aligned > accesses. Additionally, the overwriting of the max access size is > removed to retrieve the actual max access size. > > Based-on-a-patch-by: Tomoyuki Hirose <hrstmyk8...@gmail.com> > Signed-off-by: CJ Chen <cjc...@igel.co.jp> > Tested-by: CJ Chen <cjc...@igel.co.jp> > Reported-by: Tomoyuki Hirose <hrstmyk8...@gmail.com> > --- > system/memory.c | 147 ++++++++++++++++++++++++++++++++++++++--------- > system/physmem.c | 8 --- > 2 files changed, 119 insertions(+), 36 deletions(-) > > diff --git a/system/memory.c b/system/memory.c > index 63b983efcd..d6071b4414 100644 > --- a/system/memory.c > +++ b/system/memory.c > @@ -509,27 +509,118 @@ static MemTxResult > memory_region_write_with_attrs_accessor(MemoryRegion *mr, > return mr->ops->write_with_attrs(mr->opaque, addr, tmp, size, attrs); > } > > +typedef MemTxResult (*MemoryRegionAccessFn)(MemoryRegion *mr, > + hwaddr addr, > + uint64_t *value, > + unsigned size, > + signed shift, > + uint64_t mask, > + MemTxAttrs attrs);
So we now have access_emulation and access_fastpath and the function is_access_fastpath() to select between them. Can we have a comment please that says what the two are doing and what the criterion is that lets us pick the fast path ? > + > +static MemTxResult access_emulation(hwaddr addr, > + uint64_t *value, > + unsigned int size, > + unsigned int access_size_min, > + unsigned int access_size_max, > + MemoryRegion *mr, > + MemTxAttrs attrs, > + MemoryRegionAccessFn access_fn_read, > + MemoryRegionAccessFn access_fn_write, > + bool is_write) > +{ > + hwaddr a; > + uint8_t *d; > + uint64_t v; > + MemTxResult r = MEMTX_OK; > + bool is_big_endian = devend_big_endian(mr->ops->endianness); > + void (*store)(void *, int, uint64_t) = is_big_endian ? stn_be_p : > stn_le_p; > + uint64_t (*load)(const void *, int) = is_big_endian ? ldn_be_p : > ldn_le_p; Please use a typedef for all function pointers: it makes it much easier to read. > + size_t access_size = MAX(MIN(size, access_size_max), access_size_min); > + uint64_t access_mask = MAKE_64BIT_MASK(0, access_size * 8); > + hwaddr round_down = mr->ops->impl.unaligned && addr + size <= mr->size ? > + 0 : addr % access_size; > + hwaddr start = addr - round_down; > + hwaddr tail = addr + size <= mr->size ? addr + size : mr->size; > + uint8_t data[16] = {0}; > + g_assert(size <= 8); There should be a blank line after the last variable definition and before the g_assert() here. > + > + for (a = start, d = data, v = 0; a < tail; > + a += access_size, d += access_size, v = 0) { > + r |= access_fn_read(mr, a, &v, access_size, 0, access_mask, > + attrs); > + store(d, access_size, v); > + } > + if (is_write) { > + stn_he_p(&data[round_down], size, load(value, size)); > + for (a = start, d = data; a < tail; > + a += access_size, d += access_size) { > + v = load(d, access_size); > + r |= access_fn_write(mr, a, &v, access_size, 0, access_mask, > + attrs); > + } > + } else { > + store(value, size, ldn_he_p(&data[round_down], size)); > + } This would be much easier to review if there were comments that said what the intention/design of the code was. > + > + return r; > +} > + > +static bool is_access_fastpath(hwaddr addr, > + unsigned int size, > + unsigned int access_size_min, > + unsigned int access_size_max, > + MemoryRegion *mr) > +{ > + size_t access_size = MAX(MIN(size, access_size_max), access_size_min); > + hwaddr round_down = mr->ops->impl.unaligned && addr + size <= mr->size ? > + 0 : addr % access_size; > + > + return round_down == 0 && access_size <= size; > +} > + > +static MemTxResult access_fastpath(hwaddr addr, > + uint64_t *value, > + unsigned int size, > + unsigned int access_size_min, > + unsigned int access_size_max, > + MemoryRegion *mr, > + MemTxAttrs attrs, > + MemoryRegionAccessFn fastpath) > +{ > + MemTxResult r = MEMTX_OK; > + size_t access_size = MAX(MIN(size, access_size_max), access_size_min); > + uint64_t access_mask = MAKE_64BIT_MASK(0, access_size * 8); > + > + if (devend_big_endian(mr->ops->endianness)) { > + for (size_t i = 0; i < size; i += access_size) { > + r |= fastpath(mr, addr + i, value, access_size, > + (size - access_size - i) * 8, access_mask, attrs); > + } > + } else { > + for (size_t i = 0; i < size; i += access_size) { > + r |= fastpath(mr, addr + i, value, access_size, > + i * 8, access_mask, attrs); > + } > + } > + > + return r; > +} > + > static MemTxResult access_with_adjusted_size(hwaddr addr, > uint64_t *value, > unsigned size, > unsigned access_size_min, > unsigned access_size_max, > - MemTxResult (*access_fn) > - (MemoryRegion *mr, > - hwaddr addr, > - uint64_t *value, > - unsigned size, > - signed shift, > - uint64_t mask, > - MemTxAttrs attrs), > + MemoryRegionAccessFn access_fn_read, > + MemoryRegionAccessFn access_fn_write, > + bool is_write, > MemoryRegion *mr, > MemTxAttrs attrs) > { > - uint64_t access_mask; > - unsigned access_size; > - unsigned i; > MemTxResult r = MEMTX_OK; > bool reentrancy_guard_applied = false; > + MemoryRegionAccessFn access_fn_fastpath = > + is_write ? access_fn_write : access_fn_read; > > if (!access_size_min) { > access_size_min = 1; > @@ -551,20 +642,16 @@ static MemTxResult access_with_adjusted_size(hwaddr > addr, > reentrancy_guard_applied = true; > } > > - /* FIXME: support unaligned access? */ > - access_size = MAX(MIN(size, access_size_max), access_size_min); > - access_mask = MAKE_64BIT_MASK(0, access_size * 8); > - if (devend_big_endian(mr->ops->endianness)) { > - for (i = 0; i < size; i += access_size) { > - r |= access_fn(mr, addr + i, value, access_size, > - (size - access_size - i) * 8, access_mask, attrs); > - } > + if (is_access_fastpath(addr, size, access_size_min, access_size_max, > mr)) { > + r |= access_fastpath(addr, value, size, > + access_size_min, access_size_max, mr, attrs, > + access_fn_fastpath); > } else { > - for (i = 0; i < size; i += access_size) { > - r |= access_fn(mr, addr + i, value, access_size, i * 8, > - access_mask, attrs); > - } > + r |= access_emulation(addr, value, size, > + access_size_min, access_size_max, mr, attrs, > + access_fn_read, access_fn_write, is_write); > } Because you've removed the loops from this function, we don't any longer need to set r to MEMTX_OK and then OR in the return value from access_whatever; we can just set r = ... > + > if (mr->dev && reentrancy_guard_applied) { > mr->dev->mem_reentrancy_guard.engaged_in_io = false; > } thanks -- PMM