Patchwork exec.c: Fix subpage memory access to RAM MemoryRegion

login
register
mail settings
Submitter Andreas Färber
Date Nov. 28, 2011, 3:06 p.m.
Message ID <1322492805-5530-1-git-send-email-afaerber@suse.de>
Download mbox | patch
Permalink /patch/127999/
State New
Headers show

Comments

Andreas Färber - Nov. 28, 2011, 3:06 p.m.
Commit 95c318f5e1f88d7e5bcc6deac17330fd4806a2d3 (Fix segfault in mmio subpage
handling code.) prevented a segfault by making all subpage registrations
over an existing memory page perform an unassigned access. Symptoms were
writes not taking effect and reads returning zero.

Very small page sizes are not currently supported either, so subpage memory
areas cannot fully be avoided.

Therefore revert the previous fix and defer recognition of IO_MEM_RAM to
subpage_{read,write}len() and translate any access there.

Signed-off-by: Andreas Färber <afaerber@suse.de>
Cc: Avi Kivity <avi@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Cc: Blue Swirl <blauwirbel@gmail.com>
---
 exec.c |   33 +++++++++++++++++++++++++++++++--
 1 files changed, 31 insertions(+), 2 deletions(-)
Avi Kivity - Nov. 28, 2011, 5:17 p.m.
On 11/28/2011 05:06 PM, Andreas Färber wrote:
> Commit 95c318f5e1f88d7e5bcc6deac17330fd4806a2d3 (Fix segfault in mmio subpage
> handling code.) prevented a segfault by making all subpage registrations
> over an existing memory page perform an unassigned access. Symptoms were
> writes not taking effect and reads returning zero.
>
> Very small page sizes are not currently supported either, so subpage memory
> areas cannot fully be avoided.
>
> Therefore revert the previous fix and defer recognition of IO_MEM_RAM to
> subpage_{read,write}len() and translate any access there.
>
> Signed-off-by: Andreas Färber <afaerber@suse.de>
> Cc: Avi Kivity <avi@redhat.com>
> Cc: Gleb Natapov <gleb@redhat.com>
> Cc: Blue Swirl <blauwirbel@gmail.com>
> ---
>  exec.c |   33 +++++++++++++++++++++++++++++++--
>  1 files changed, 31 insertions(+), 2 deletions(-)
>
> diff --git a/exec.c b/exec.c
> index 6b92198..fba5ba1 100644
> --- a/exec.c
> +++ b/exec.c
> @@ -3508,6 +3508,21 @@ static inline uint32_t subpage_readlen (subpage_t *mmio,
>  
>      addr += mmio->region_offset[idx];
>      idx = mmio->sub_io_index[idx];
> +    if (unlikely(idx == IO_MEM_RAM)) {

IMO, io_mem_init() should have something like

  cpu_register_io_memory_fixed(IO_MEM_SUBPAGE_RAM, subpage_ram_read,
subpage_ram_write, ...);

so you don't need those ugly switches; you just convert IO_MEM_RAM to
IO_MEM_SUBPAGE_RAM.  Maybe even register IO_MEM_RAM itself.  Note need
to handle dirty logging carefully.
Andreas Färber - Nov. 28, 2011, 10:39 p.m.
Am 28.11.2011 18:17, schrieb Avi Kivity:
> On 11/28/2011 05:06 PM, Andreas Färber wrote:
>> Commit 95c318f5e1f88d7e5bcc6deac17330fd4806a2d3 (Fix segfault in mmio subpage
>> handling code.) prevented a segfault by making all subpage registrations
>> over an existing memory page perform an unassigned access. Symptoms were
>> writes not taking effect and reads returning zero.
>>
>> Very small page sizes are not currently supported either, so subpage memory
>> areas cannot fully be avoided.
>>
>> Therefore revert the previous fix and defer recognition of IO_MEM_RAM to
>> subpage_{read,write}len() and translate any access there.
>>
>> Signed-off-by: Andreas Färber <afaerber@suse.de>
>> Cc: Avi Kivity <avi@redhat.com>
>> Cc: Gleb Natapov <gleb@redhat.com>
>> Cc: Blue Swirl <blauwirbel@gmail.com>
>> ---
>>  exec.c |   33 +++++++++++++++++++++++++++++++--
>>  1 files changed, 31 insertions(+), 2 deletions(-)
>>
>> diff --git a/exec.c b/exec.c
>> index 6b92198..fba5ba1 100644
>> --- a/exec.c
>> +++ b/exec.c
>> @@ -3508,6 +3508,21 @@ static inline uint32_t subpage_readlen (subpage_t *mmio,
>>  
>>      addr += mmio->region_offset[idx];
>>      idx = mmio->sub_io_index[idx];
>> +    if (unlikely(idx == IO_MEM_RAM)) {
> 
> IMO, io_mem_init() should have something like
> 
>   cpu_register_io_memory_fixed(IO_MEM_SUBPAGE_RAM, subpage_ram_read,
> subpage_ram_write, ...);
> 
> so you don't need those ugly switches; you just convert IO_MEM_RAM to
> IO_MEM_SUBPAGE_RAM.  Maybe even register IO_MEM_RAM itself.  Note need
> to handle dirty logging carefully.

That didn't work because cpu_register_io_memory_fixed() is called from
subpage_init(), which is called once for the whole page only, and the
actual subpages are set up with multiple calls to subpage_register()
instead.

Andreas
Avi Kivity - Nov. 29, 2011, 9:52 a.m.
On 11/29/2011 12:39 AM, Andreas Färber wrote:
> Am 28.11.2011 18:17, schrieb Avi Kivity:
> > On 11/28/2011 05:06 PM, Andreas Färber wrote:
> >> Commit 95c318f5e1f88d7e5bcc6deac17330fd4806a2d3 (Fix segfault in mmio subpage
> >> handling code.) prevented a segfault by making all subpage registrations
> >> over an existing memory page perform an unassigned access. Symptoms were
> >> writes not taking effect and reads returning zero.
> >>
> >> Very small page sizes are not currently supported either, so subpage memory
> >> areas cannot fully be avoided.
> >>
> >> Therefore revert the previous fix and defer recognition of IO_MEM_RAM to
> >> subpage_{read,write}len() and translate any access there.
> >>
> >> Signed-off-by: Andreas Färber <afaerber@suse.de>
> >> Cc: Avi Kivity <avi@redhat.com>
> >> Cc: Gleb Natapov <gleb@redhat.com>
> >> Cc: Blue Swirl <blauwirbel@gmail.com>
> >> ---
> >>  exec.c |   33 +++++++++++++++++++++++++++++++--
> >>  1 files changed, 31 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/exec.c b/exec.c
> >> index 6b92198..fba5ba1 100644
> >> --- a/exec.c
> >> +++ b/exec.c
> >> @@ -3508,6 +3508,21 @@ static inline uint32_t subpage_readlen (subpage_t *mmio,
> >>  
> >>      addr += mmio->region_offset[idx];
> >>      idx = mmio->sub_io_index[idx];
> >> +    if (unlikely(idx == IO_MEM_RAM)) {
> > 
> > IMO, io_mem_init() should have something like
> > 
> >   cpu_register_io_memory_fixed(IO_MEM_SUBPAGE_RAM, subpage_ram_read,
> > subpage_ram_write, ...);
> > 
> > so you don't need those ugly switches; you just convert IO_MEM_RAM to
> > IO_MEM_SUBPAGE_RAM.  Maybe even register IO_MEM_RAM itself.  Note need
> > to handle dirty logging carefully.
>
> That didn't work because cpu_register_io_memory_fixed() is called from
> subpage_init(), which is called once for the whole page only, and the
> actual subpages are set up with multiple calls to subpage_register()
> instead.

I don't mean replacing the subpage handle with a call to c_r_io_m_f();
just make the handle that is placed supage_t::sub_io_index have real io
callbacks.

In io_mem_init(), call cpu_register_io_memory_fixed() with a new
mem_read[] callback array an the existing notdirty_mem_write[] array. 
In subpage_register(), if we get an IO_MEM_RAM, convert it to
IO_MEM_SUBPAGE_RAM (and copy the 'memory' to region_offset).
Andreas Färber - Nov. 29, 2011, 12:47 p.m.
Am 28.11.2011 16:06, schrieb Andreas Färber:
> Commit 95c318f5e1f88d7e5bcc6deac17330fd4806a2d3 (Fix segfault in mmio subpage
> handling code.) prevented a segfault by making all subpage registrations
> over an existing memory page perform an unassigned access. Symptoms were
> writes not taking effect and reads returning zero.
> 
> Very small page sizes are not currently supported either, so subpage memory
> areas cannot fully be avoided.
> 
> Therefore revert the previous fix and defer recognition of IO_MEM_RAM to
> subpage_{read,write}len() and translate any access there.
> 
> Signed-off-by: Andreas Färber <afaerber@suse.de>
> Cc: Avi Kivity <avi@redhat.com>
> Cc: Gleb Natapov <gleb@redhat.com>
> Cc: Blue Swirl <blauwirbel@gmail.com>
> ---
>  exec.c |   33 +++++++++++++++++++++++++++++++--
>  1 files changed, 31 insertions(+), 2 deletions(-)
> 
> diff --git a/exec.c b/exec.c
> index 6b92198..fba5ba1 100644
> --- a/exec.c
> +++ b/exec.c
> @@ -3508,6 +3508,21 @@ static inline uint32_t subpage_readlen (subpage_t *mmio,
>  
>      addr += mmio->region_offset[idx];
>      idx = mmio->sub_io_index[idx];
> +    if (unlikely(idx == IO_MEM_RAM)) {
> +        ram_addr_t raddr = /*mmio->base |*/ addr;
> +        void *ptr = qemu_get_ram_ptr(raddr);

This...

> +        switch (len) {
> +        default:
> +        case 0:
> +            return ldub_p(ptr);
> +        case 1:
> +            return lduw_p(ptr);
> +        case 2:
> +            return ldl_p(ptr);
> +        case 3:
> +            return ldq_p(ptr);
> +        }
> +    }
>      return io_mem_read[idx][len](io_mem_opaque[idx], addr);
>  }
>  
> @@ -3522,6 +3537,22 @@ static inline void subpage_writelen (subpage_t *mmio, target_phys_addr_t addr,
>  
>      addr += mmio->region_offset[idx];
>      idx = mmio->sub_io_index[idx];
> +    if (unlikely(idx == IO_MEM_RAM)) {
> +        ram_addr_t raddr = /*mmio->base |*/ addr;
> +        void *ptr = qemu_get_ram_ptr(raddr);

...and/or this seems to lead to "Bad RAM pointer" (or so) when there's
ELF code loaded into the subpage at that address despite being IO_MEM_RAM?
(Seen, e.g., if for RL78 I increase the page size from 12 to 16.)

> +        switch (len) {
> +        default:
> +        case 0:
> +            stb_p(ptr, value);

break;

> +        case 1:
> +            stw_p(ptr, value);

break;

> +        case 2:
> +            stl_p(ptr, value);

break;

> +        case 3:
> +            stq_p(ptr, value);

break;

Andreas

> +        }
> +        return;
> +    }
>      io_mem_write[idx][len](io_mem_opaque[idx], addr, value);
>  }
>  
> @@ -3583,8 +3614,6 @@ static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
>      printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %ld\n", __func__,
>             mmio, start, end, idx, eidx, memory);
>  #endif
> -    if ((memory & ~TARGET_PAGE_MASK) == IO_MEM_RAM)
> -        memory = IO_MEM_UNASSIGNED;
>      memory = (memory >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
>      for (; idx <= eidx; idx++) {
>          mmio->sub_io_index[idx] = memory;
Avi Kivity - Nov. 29, 2011, 2 p.m.
On 11/29/2011 02:47 PM, Andreas Färber wrote:
> >  
> >      addr += mmio->region_offset[idx];
> >      idx = mmio->sub_io_index[idx];
> > +    if (unlikely(idx == IO_MEM_RAM)) {
> > +        ram_addr_t raddr = /*mmio->base |*/ addr;
> > +        void *ptr = qemu_get_ram_ptr(raddr);
>
> ...and/or this seems to lead to "Bad RAM pointer" (or so) when there's
> ELF code loaded into the subpage at that address despite being IO_MEM_RAM?
> (Seen, e.g., if for RL78 I increase the page size from 12 to 16.)
>

addr is relative to the start of the page, instead of the start of the
region.  You can use mmio->region_offset[idx] to compensate.
Andreas Färber - Nov. 29, 2011, 4:19 p.m.
Am 29.11.2011 15:00, schrieb Avi Kivity:
> On 11/29/2011 02:47 PM, Andreas Färber wrote:
>>>  
>>>      addr += mmio->region_offset[idx];
>>>      idx = mmio->sub_io_index[idx];
>>> +    if (unlikely(idx == IO_MEM_RAM)) {
>>> +        ram_addr_t raddr = /*mmio->base |*/ addr;
>>> +        void *ptr = qemu_get_ram_ptr(raddr);
>>
>> ...and/or this seems to lead to "Bad RAM pointer" (or so) when there's
>> ELF code loaded into the subpage at that address despite being IO_MEM_RAM?
>> (Seen, e.g., if for RL78 I increase the page size from 12 to 16.)
>>
> 
> addr is relative to the start of the page, instead of the start of the
> region.  You can use mmio->region_offset[idx] to compensate.

That's exactly what the first line of the snippet above does, no?
(sorry, there's still a confusing comment from an earlier attempt)

Andreas
Avi Kivity - Nov. 29, 2011, 4:46 p.m.
On 11/29/2011 06:19 PM, Andreas Färber wrote:
> Am 29.11.2011 15:00, schrieb Avi Kivity:
> > On 11/29/2011 02:47 PM, Andreas Färber wrote:
> >>>  
> >>>      addr += mmio->region_offset[idx];
> >>>      idx = mmio->sub_io_index[idx];
> >>> +    if (unlikely(idx == IO_MEM_RAM)) {
> >>> +        ram_addr_t raddr = /*mmio->base |*/ addr;
> >>> +        void *ptr = qemu_get_ram_ptr(raddr);
> >>
> >> ...and/or this seems to lead to "Bad RAM pointer" (or so) when there's
> >> ELF code loaded into the subpage at that address despite being IO_MEM_RAM?
> >> (Seen, e.g., if for RL78 I increase the page size from 12 to 16.)
> >>
> > 
> > addr is relative to the start of the page, instead of the start of the
> > region.  You can use mmio->region_offset[idx] to compensate.
>
> That's exactly what the first line of the snippet above does, no?
> (sorry, there's still a confusing comment from an earlier attempt)
>

Depends on whether ->region_offset has the right value or not, where's
the code that adjusts it?  For your case that does f00-fff it should be
-0xf00 + ram_addr (of the start of the region).
Andreas Färber - Nov. 30, 2011, 3:34 p.m.
Am 29.11.2011 10:52, schrieb Avi Kivity:
> On 11/29/2011 12:39 AM, Andreas Färber wrote:
>> Am 28.11.2011 18:17, schrieb Avi Kivity:
>>> On 11/28/2011 05:06 PM, Andreas Färber wrote:
>>>> diff --git a/exec.c b/exec.c
>>>> index 6b92198..fba5ba1 100644
>>>> --- a/exec.c
>>>> +++ b/exec.c
>>>> @@ -3508,6 +3508,21 @@ static inline uint32_t subpage_readlen (subpage_t *mmio,
>>>>  
>>>>      addr += mmio->region_offset[idx];
>>>>      idx = mmio->sub_io_index[idx];
>>>> +    if (unlikely(idx == IO_MEM_RAM)) {
>>>
>>> IMO, io_mem_init() should have something like
>>>
>>>   cpu_register_io_memory_fixed(IO_MEM_SUBPAGE_RAM, subpage_ram_read,
>>> subpage_ram_write, ...);
>>>
>>> so you don't need those ugly switches; you just convert IO_MEM_RAM to
>>> IO_MEM_SUBPAGE_RAM.  Maybe even register IO_MEM_RAM itself.  Note need
>>> to handle dirty logging carefully.
>>
>> That didn't work because cpu_register_io_memory_fixed() is called from
>> subpage_init(), which is called once for the whole page only, and the
>> actual subpages are set up with multiple calls to subpage_register()
>> instead.
> 
> I don't mean replacing the subpage handle with a call to c_r_io_m_f();
> just make the handle that is placed supage_t::sub_io_index have real io
> callbacks.
> 
> In io_mem_init(), call cpu_register_io_memory_fixed() with a new
> mem_read[] callback array an the existing notdirty_mem_write[] array. 
> In subpage_register(), if we get an IO_MEM_RAM, convert it to
> IO_MEM_SUBPAGE_RAM (and copy the 'memory' to region_offset).

Sorry, I simply misunderstood your suggestion (memory newbie): Since we
*continue* to go through subpage_{read,write}len(), we don't need a
specific opaque value any longer so can do this from generic code.

v2 sent out.

Andreas
Andreas Färber - Nov. 30, 2011, 4:22 p.m.
Am 29.11.2011 17:46, schrieb Avi Kivity:
> On 11/29/2011 06:19 PM, Andreas Färber wrote:
>> Am 29.11.2011 15:00, schrieb Avi Kivity:
>>> On 11/29/2011 02:47 PM, Andreas Färber wrote:
>>>>>  
>>>>>      addr += mmio->region_offset[idx];
>>>>>      idx = mmio->sub_io_index[idx];
>>>>> +    if (unlikely(idx == IO_MEM_RAM)) {
>>>>> +        ram_addr_t raddr = /*mmio->base |*/ addr;
>>>>> +        void *ptr = qemu_get_ram_ptr(raddr);
>>>>
>>>> ...and/or this seems to lead to "Bad RAM pointer" (or so) when there's
>>>> ELF code loaded into the subpage at that address despite being IO_MEM_RAM?
>>>> (Seen, e.g., if for RL78 I increase the page size from 12 to 16.)
>>>>
>>>
>>> addr is relative to the start of the page, instead of the start of the
>>> region.  You can use mmio->region_offset[idx] to compensate.
>>
>> That's exactly what the first line of the snippet above does, no?
>> (sorry, there's still a confusing comment from an earlier attempt)
>>
> 
> Depends on whether ->region_offset has the right value or not, where's
> the code that adjusts it?  For your case that does f00-fff it should be
> -0xf00 + ram_addr (of the start of the region).

The problem is not with the 0xfff00 region, that one works fine with 12
and 16 bit pages so far.

What I'm seeing is a crash of the very first TB at PC 0x02010, which is
in a 4K RAM region from 0x0 on, with 16 bit pages. Also happens with v2.

It's similar to my original problem of being unable to read the reset
vector, where I couldn't use the regular memory read functions but had
to use rom_ptr(), as Peter pointed out in
target-arm/helper.c:cpu_reset(). Apparently I'll need to add some check
for handling ROM, but on what condition and where? Don't really
understand why loading ELF code into a RAM area creates these weird ROM
areas... Is this a legacy thing, as it's not shown in info mtree?

(qemu) info roms
addr=00000000 size=0x000002 mem=rom name="phdr #0:
/home/andreas/MCU/RL78/test"
addr=00000004 size=0x00007c mem=rom name="phdr #1:
/home/andreas/MCU/RL78/test"
addr=000000c0 size=0x000004 mem=rom name="phdr #2:
/home/andreas/MCU/RL78/test"
addr=000000d8 size=0x000004 mem=rom name="phdr #3:
/home/andreas/MCU/RL78/test"
addr=000000dc size=0x00026a mem=rom name="phdr #4:
/home/andreas/MCU/RL78/test"
addr=00002000 size=0x0004e4 mem=rom name="phdr #5:
/home/andreas/MCU/RL78/test"
(qemu) cont

Bad ram pointer 0x2010

Program received signal SIGABRT, Aborted.
[Switching to Thread 0x7ffff5504700 (LWP 6484)]
0x00007ffff617ad95 in raise () from /lib64/libc.so.6
(gdb) bt
#0  0x00007ffff617ad95 in raise () from /lib64/libc.so.6
#1  0x00007ffff617c2ab in abort () from /lib64/libc.so.6
#2  0x00007ffff7f76928 in qemu_ram_addr_from_host_nofail (ptr=0x2010)
    at /home/andreas/QEMU/qemu-rl78/exec.c:3248
#3  qemu_ram_addr_from_host_nofail (ptr=0x2010)
    at /home/andreas/QEMU/qemu-rl78/exec.c:3242
#4  0x00007ffff7f7036d in get_page_addr_code (addr=8208,
env1=0x7ffff8b04010)
    at /home/andreas/QEMU/qemu-rl78/exec-all.h:362
#5  tb_find_slow (pc=8208, env=0x7ffff8b04010, cs_base=<optimized out>,
    flags=<optimized out>) at /home/andreas/QEMU/qemu-rl78/cpu-exec.c:95
#6  tb_find_fast (env=0x7ffff8b04010)
    at /home/andreas/QEMU/qemu-rl78/cpu-exec.c:151
#7  cpu_78k0_exec (env=0x7ffff8b04010)
    at /home/andreas/QEMU/qemu-rl78/cpu-exec.c:535
#8  0x00007ffff7f7181c in tcg_cpu_exec (env=0x7ffff8b04010)
    at /home/andreas/QEMU/qemu-rl78/cpus.c:1007
#9  tcg_exec_all () at /home/andreas/QEMU/qemu-rl78/cpus.c:1039
#10 qemu_tcg_cpu_thread_fn (arg=<optimized out>)
    at /home/andreas/QEMU/qemu-rl78/cpus.c:774
#11 0x00007ffff6c8cf05 in start_thread () from /lib64/libpthread.so.0
#12 0x00007ffff621f53d in clone () from /lib64/libc.so.6

Andreas
Avi Kivity - Dec. 1, 2011, 9:24 a.m.
On 11/30/2011 06:22 PM, Andreas Färber wrote:
> The problem is not with the 0xfff00 region, that one works fine with 12
> and 16 bit pages so far.
>
> What I'm seeing is a crash of the very first TB at PC 0x02010, which is
> in a 4K RAM region from 0x0 on, with 16 bit pages. Also happens with v2.

Executing out of subpage RAM is probably unique enough to have its own
set of weird and wonderful bugs.

Since it's the very first TB, you could simply add tons of
instrumentation and just follow the guest code and see what it does.

> It's similar to my original problem of being unable to read the reset
> vector, where I couldn't use the regular memory read functions but had
> to use rom_ptr(), as Peter pointed out in
> target-arm/helper.c:cpu_reset(). Apparently I'll need to add some check
> for handling ROM, but on what condition and where? Don't really
> understand why loading ELF code into a RAM area creates these weird ROM
> areas... Is this a legacy thing, as it's not shown in info mtree?

Everything should be seen in 'info mtree', at least for everything
that's been converted.  You could try the memory/master branch where
this is true.  Did you add roms yourself?

>
> (qemu) info roms
> addr=00000000 size=0x000002 mem=rom name="phdr #0:
> /home/andreas/MCU/RL78/test"
> addr=00000004 size=0x00007c mem=rom name="phdr #1:
> /home/andreas/MCU/RL78/test"
> addr=000000c0 size=0x000004 mem=rom name="phdr #2:
> /home/andreas/MCU/RL78/test"
> addr=000000d8 size=0x000004 mem=rom name="phdr #3:
> /home/andreas/MCU/RL78/test"
> addr=000000dc size=0x00026a mem=rom name="phdr #4:
> /home/andreas/MCU/RL78/test"
> addr=00002000 size=0x0004e4 mem=rom name="phdr #5:
> /home/andreas/MCU/RL78/test"
> (qemu) cont
>
> Bad ram pointer 0x2010
>
> Program received signal SIGABRT, Aborted.
> [Switching to Thread 0x7ffff5504700 (LWP 6484)]
> 0x00007ffff617ad95 in raise () from /lib64/libc.so.6
> (gdb) bt
> #0  0x00007ffff617ad95 in raise () from /lib64/libc.so.6
> #1  0x00007ffff617c2ab in abort () from /lib64/libc.so.6
> #2  0x00007ffff7f76928 in qemu_ram_addr_from_host_nofail (ptr=0x2010)
>     at /home/andreas/QEMU/qemu-rl78/exec.c:3248
> #3  qemu_ram_addr_from_host_nofail (ptr=0x2010)
>     at /home/andreas/QEMU/qemu-rl78/exec.c:3242
> #4  0x00007ffff7f7036d in get_page_addr_code (addr=8208,
> env1=0x7ffff8b04010)
>

This function tries to translate a virtual guest address of a page into
a ram address, but that doesn't work for subpage.

    if (pd > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
#if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SPARC)
        cpu_unassigned_access(env1, addr, 0, 1, 0, 4);
#else
        cpu_abort(env1, "Trying to execute code outside RAM or ROM at
0x" TARGET_FMT_lx "\n", addr);
#endif

You're clearly in RAM or ROM, but the subpage code confuses qemu.  It's
not going to be easy to unconfuse it.

Patch

diff --git a/exec.c b/exec.c
index 6b92198..fba5ba1 100644
--- a/exec.c
+++ b/exec.c
@@ -3508,6 +3508,21 @@  static inline uint32_t subpage_readlen (subpage_t *mmio,
 
     addr += mmio->region_offset[idx];
     idx = mmio->sub_io_index[idx];
+    if (unlikely(idx == IO_MEM_RAM)) {
+        ram_addr_t raddr = /*mmio->base |*/ addr;
+        void *ptr = qemu_get_ram_ptr(raddr);
+        switch (len) {
+        default:
+        case 0:
+            return ldub_p(ptr);
+        case 1:
+            return lduw_p(ptr);
+        case 2:
+            return ldl_p(ptr);
+        case 3:
+            return ldq_p(ptr);
+        }
+    }
     return io_mem_read[idx][len](io_mem_opaque[idx], addr);
 }
 
@@ -3522,6 +3537,22 @@  static inline void subpage_writelen (subpage_t *mmio, target_phys_addr_t addr,
 
     addr += mmio->region_offset[idx];
     idx = mmio->sub_io_index[idx];
+    if (unlikely(idx == IO_MEM_RAM)) {
+        ram_addr_t raddr = /*mmio->base |*/ addr;
+        void *ptr = qemu_get_ram_ptr(raddr);
+        switch (len) {
+        default:
+        case 0:
+            stb_p(ptr, value);
+        case 1:
+            stw_p(ptr, value);
+        case 2:
+            stl_p(ptr, value);
+        case 3:
+            stq_p(ptr, value);
+        }
+        return;
+    }
     io_mem_write[idx][len](io_mem_opaque[idx], addr, value);
 }
 
@@ -3583,8 +3614,6 @@  static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
     printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %ld\n", __func__,
            mmio, start, end, idx, eidx, memory);
 #endif
-    if ((memory & ~TARGET_PAGE_MASK) == IO_MEM_RAM)
-        memory = IO_MEM_UNASSIGNED;
     memory = (memory >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
     for (; idx <= eidx; idx++) {
         mmio->sub_io_index[idx] = memory;