diff mbox

[v2] exec.c: Fix subpage memory access to RAM MemoryRegion

Message ID 1322666781-6108-1-git-send-email-afaerber@suse.de
State New
Headers show

Commit Message

Andreas Färber Nov. 30, 2011, 3:26 p.m. UTC
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 change the previous fix to use a new IO_MEM_SUBPAGE_RAM
instead of IO_MEM_UNASSIGNED. Suggested by Avi.

Signed-off-by: Andreas Färber <afaerber@suse.de>
Cc: Avi Kivity <avi@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
---
 cpu-common.h |    1 +
 exec.c       |   65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 64 insertions(+), 2 deletions(-)

Comments

Avi Kivity Dec. 1, 2011, 9:29 a.m. UTC | #1
On 11/30/2011 05:26 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 change the previous fix to use a new IO_MEM_SUBPAGE_RAM
> instead of IO_MEM_UNASSIGNED. Suggested by Avi.
>
>

Looks reasonable.  Should go into 1.1.  Should we backport it to
1.0.blah?  From 95c318f's description, it doesn't happen in normal
circumstances.
Gleb Natapov Dec. 1, 2011, 9:37 a.m. UTC | #2
On Thu, Dec 01, 2011 at 11:29:48AM +0200, Avi Kivity wrote:
> On 11/30/2011 05:26 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 change the previous fix to use a new IO_MEM_SUBPAGE_RAM
> > instead of IO_MEM_UNASSIGNED. Suggested by Avi.
> >
> >
> 
> Looks reasonable.  Should go into 1.1.  Should we backport it to
> 1.0.blah?  From 95c318f's description, it doesn't happen in normal
> circumstances.
> 
To reproduce that I mappped subpage PCI bar over RAM IIRC. For KVM
the memory in a subpage will not be accessible even with this fix since
memory slots have page granularity and KVM can't execute code from MMIO
(yet?). Andreas do you have real scenario where this fix is needed?

--
			Gleb.
Avi Kivity Dec. 1, 2011, 9:41 a.m. UTC | #3
On 12/01/2011 11:37 AM, Gleb Natapov wrote:
> > 
> > Looks reasonable.  Should go into 1.1.  Should we backport it to
> > 1.0.blah?  From 95c318f's description, it doesn't happen in normal
> > circumstances.
> > 
> To reproduce that I mappped subpage PCI bar over RAM IIRC. 

In qemu 1.0, you can no longer do that (the pci bridge will not let the
BAR override the RAM).
Gleb Natapov Dec. 1, 2011, 9:47 a.m. UTC | #4
On Thu, Dec 01, 2011 at 11:41:52AM +0200, Avi Kivity wrote:
> On 12/01/2011 11:37 AM, Gleb Natapov wrote:
> > > 
> > > Looks reasonable.  Should go into 1.1.  Should we backport it to
> > > 1.0.blah?  From 95c318f's description, it doesn't happen in normal
> > > circumstances.
> > > 
> > To reproduce that I mappped subpage PCI bar over RAM IIRC. 
> 
> In qemu 1.0, you can no longer do that (the pci bridge will not let the
> BAR override the RAM).
> 

Hmm, if this is how real HW work then problem solved :) (different HW can
behave differently, but it is reasonable to assume that on a PC memory
access below TOM will be redirected to memory controller no matter what)
So what is the motivation for Andreas patch than?

--
			Gleb.
Avi Kivity Dec. 1, 2011, 9:54 a.m. UTC | #5
On 12/01/2011 11:47 AM, Gleb Natapov wrote:
> On Thu, Dec 01, 2011 at 11:41:52AM +0200, Avi Kivity wrote:
> > On 12/01/2011 11:37 AM, Gleb Natapov wrote:
> > > > 
> > > > Looks reasonable.  Should go into 1.1.  Should we backport it to
> > > > 1.0.blah?  From 95c318f's description, it doesn't happen in normal
> > > > circumstances.
> > > > 
> > > To reproduce that I mappped subpage PCI bar over RAM IIRC. 
> > 
> > In qemu 1.0, you can no longer do that (the pci bridge will not let the
> > BAR override the RAM).
> > 
>
> Hmm, if this is how real HW work then problem solved :) (different HW can
> behave differently, but it is reasonable to assume that on a PC memory
> access below TOM will be redirected to memory controller no matter what)
> So what is the motivation for Andreas patch than?
>

He's not emulating pc hardware.
Gleb Natapov Dec. 1, 2011, 10:06 a.m. UTC | #6
On Thu, Dec 01, 2011 at 11:54:33AM +0200, Avi Kivity wrote:
> On 12/01/2011 11:47 AM, Gleb Natapov wrote:
> > On Thu, Dec 01, 2011 at 11:41:52AM +0200, Avi Kivity wrote:
> > > On 12/01/2011 11:37 AM, Gleb Natapov wrote:
> > > > > 
> > > > > Looks reasonable.  Should go into 1.1.  Should we backport it to
> > > > > 1.0.blah?  From 95c318f's description, it doesn't happen in normal
> > > > > circumstances.
> > > > > 
> > > > To reproduce that I mappped subpage PCI bar over RAM IIRC. 
> > > 
> > > In qemu 1.0, you can no longer do that (the pci bridge will not let the
> > > BAR override the RAM).
> > > 
> >
> > Hmm, if this is how real HW work then problem solved :) (different HW can
> > behave differently, but it is reasonable to assume that on a PC memory
> > access below TOM will be redirected to memory controller no matter what)
> > So what is the motivation for Andreas patch than?
> >
> 
> He's not emulating pc hardware.
> 
That's not a crime in itself :) What HW he encountered this problem on?
What scenario? How likely is this scenario on that HW (my comment for
95c318f which you are referring to above was for PC)? And if KVM is
supported on that HW my comment about KVM still applies.

--
			Gleb.
Andreas Färber Dec. 1, 2011, 5:18 p.m. UTC | #7
Am 01.12.2011 11:06, schrieb Gleb Natapov:
> On Thu, Dec 01, 2011 at 11:54:33AM +0200, Avi Kivity wrote:
>> On 12/01/2011 11:47 AM, Gleb Natapov wrote:
>>> On Thu, Dec 01, 2011 at 11:41:52AM +0200, Avi Kivity wrote:
>>>> On 12/01/2011 11:37 AM, Gleb Natapov wrote:
>>>>>>
>>>>>> Looks reasonable.  Should go into 1.1.  Should we backport it to
>>>>>> 1.0.blah?  From 95c318f's description, it doesn't happen in normal
>>>>>> circumstances.
>>>>>>
>>>>> To reproduce that I mappped subpage PCI bar over RAM IIRC. 
>>>>
>>>> In qemu 1.0, you can no longer do that (the pci bridge will not let the
>>>> BAR override the RAM).
>>>>
>>>
>>> Hmm, if this is how real HW work then problem solved :) (different HW can
>>> behave differently, but it is reasonable to assume that on a PC memory
>>> access below TOM will be redirected to memory controller no matter what)

Ah, glad to know that x86_64 is no longer affected. What about 0.15.1?

>>> So what is the motivation for Andreas patch than?
>>>
>>
>> He's not emulating pc hardware.
>>
> That's not a crime in itself :) What HW he encountered this problem on?
> What scenario? How likely is this scenario on that HW (my comment for
> 95c318f which you are referring to above was for PC)?

I encountered this on a nommu architecture that's not yet upstream (78k0
family / rl78). The exact scenario was a 256-byte long RAM area for
Special Function Registers (fixable by 8-bit pages) and a 32-byte long
RAM subarea for memory-mapped banked GPRs (not fixable by lowering page
size to 5, doesn't build).

I'm aware that the former I could convert to mmio and the latter I might
drop but that's besides the point, it's not prohibited by MemoryRegion
API and silently fails unless DEBUG_UNASSIGNED enabled. Seems worth a fix.

Upstream potential no-mmu architectures and their target page sizes are:
lm32 (12)
m68k (10)
microblaze (12)
mips (12)
xtensa (12)

> And if KVM is
> supported on that HW my comment about KVM still applies.

I don't think KVM is supported on any of the above.

Andreas
Avi Kivity Dec. 1, 2011, 5:24 p.m. UTC | #8
On 12/01/2011 07:18 PM, Andreas Färber wrote:
> Am 01.12.2011 11:06, schrieb Gleb Natapov:
> > On Thu, Dec 01, 2011 at 11:54:33AM +0200, Avi Kivity wrote:
> >> On 12/01/2011 11:47 AM, Gleb Natapov wrote:
> >>> On Thu, Dec 01, 2011 at 11:41:52AM +0200, Avi Kivity wrote:
> >>>> On 12/01/2011 11:37 AM, Gleb Natapov wrote:
> >>>>>>
> >>>>>> Looks reasonable.  Should go into 1.1.  Should we backport it to
> >>>>>> 1.0.blah?  From 95c318f's description, it doesn't happen in normal
> >>>>>> circumstances.
> >>>>>>
> >>>>> To reproduce that I mappped subpage PCI bar over RAM IIRC. 
> >>>>
> >>>> In qemu 1.0, you can no longer do that (the pci bridge will not let the
> >>>> BAR override the RAM).
> >>>>
> >>>
> >>> Hmm, if this is how real HW work then problem solved :) (different HW can
> >>> behave differently, but it is reasonable to assume that on a PC memory
> >>> access below TOM will be redirected to memory controller no matter what)
>
> Ah, glad to know that x86_64 is no longer affected. What about 0.15.1?

In 0.15 this can still happen, 1.0 was fixed by the memory API.  Note
the fix is actually a side effect of a different change, and the issue
can still happen for other reasons (as in your case).
Andreas Färber Dec. 9, 2011, 12:32 p.m. UTC | #9
Am 01.12.2011 10:29, schrieb Avi Kivity:
> On 11/30/2011 05:26 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 change the previous fix to use a new IO_MEM_SUBPAGE_RAM
>> instead of IO_MEM_UNASSIGNED. Suggested by Avi.
>>
>>
> 
> Looks reasonable.  Should go into 1.1.

A formal Reviewed-by or Acked-by may help. :)

> Should we backport it to
> 1.0.blah?  From 95c318f's description, it doesn't happen in normal
> circumstances.

We feel it should be backported to both 1.0.x and 0.15.x, please.

Andreas
Avi Kivity Dec. 11, 2011, 9:51 a.m. UTC | #10
On 11/30/2011 05:26 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 change the previous fix to use a new IO_MEM_SUBPAGE_RAM
> instead of IO_MEM_UNASSIGNED. Suggested by Avi.
>

Reviewed-by: Avi Kivity <avi@redhat.com>
Anthony Liguori Dec. 15, 2011, 6:09 p.m. UTC | #11
On 11/30/2011 09:26 AM, 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 change the previous fix to use a new IO_MEM_SUBPAGE_RAM
> instead of IO_MEM_UNASSIGNED. Suggested by Avi.
>
> Signed-off-by: Andreas Färber<afaerber@suse.de>
> Cc: Avi Kivity<avi@redhat.com>
> Cc: Gleb Natapov<gleb@redhat.com>

Applied.  Thanks.

Regards,

Anthony Liguori

> ---
>   cpu-common.h |    1 +
>   exec.c       |   65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>   2 files changed, 64 insertions(+), 2 deletions(-)
>
> diff --git a/cpu-common.h b/cpu-common.h
> index c9878ba..3f45428 100644
> --- a/cpu-common.h
> +++ b/cpu-common.h
> @@ -172,6 +172,7 @@ void cpu_physical_memory_write_rom(target_phys_addr_t addr,
>   #define IO_MEM_ROM         (1<<  IO_MEM_SHIFT) /* hardcoded offset */
>   #define IO_MEM_UNASSIGNED  (2<<  IO_MEM_SHIFT)
>   #define IO_MEM_NOTDIRTY    (3<<  IO_MEM_SHIFT)
> +#define IO_MEM_SUBPAGE_RAM (4<<  IO_MEM_SHIFT)
>
>   /* Acts like a ROM when read and like a device when written.  */
>   #define IO_MEM_ROMD        (1)
> diff --git a/exec.c b/exec.c
> index 6b92198..6c206ff 100644
> --- a/exec.c
> +++ b/exec.c
> @@ -3570,6 +3570,63 @@ static CPUWriteMemoryFunc * const subpage_write[] = {
>       &subpage_writel,
>   };
>
> +static uint32_t subpage_ram_readb(void *opaque, target_phys_addr_t addr)
> +{
> +    ram_addr_t raddr = addr;
> +    void *ptr = qemu_get_ram_ptr(raddr);
> +    return ldub_p(ptr);
> +}
> +
> +static void subpage_ram_writeb(void *opaque, target_phys_addr_t addr,
> +                               uint32_t value)
> +{
> +    ram_addr_t raddr = addr;
> +    void *ptr = qemu_get_ram_ptr(raddr);
> +    stb_p(ptr, value);
> +}
> +
> +static uint32_t subpage_ram_readw(void *opaque, target_phys_addr_t addr)
> +{
> +    ram_addr_t raddr = addr;
> +    void *ptr = qemu_get_ram_ptr(raddr);
> +    return lduw_p(ptr);
> +}
> +
> +static void subpage_ram_writew(void *opaque, target_phys_addr_t addr,
> +                               uint32_t value)
> +{
> +    ram_addr_t raddr = addr;
> +    void *ptr = qemu_get_ram_ptr(raddr);
> +    stw_p(ptr, value);
> +}
> +
> +static uint32_t subpage_ram_readl(void *opaque, target_phys_addr_t addr)
> +{
> +    ram_addr_t raddr = addr;
> +    void *ptr = qemu_get_ram_ptr(raddr);
> +    return ldl_p(ptr);
> +}
> +
> +static void subpage_ram_writel(void *opaque, target_phys_addr_t addr,
> +                               uint32_t value)
> +{
> +    ram_addr_t raddr = addr;
> +    void *ptr = qemu_get_ram_ptr(raddr);
> +    stl_p(ptr, value);
> +}
> +
> +static CPUReadMemoryFunc * const subpage_ram_read[] = {
> +&subpage_ram_readb,
> +&subpage_ram_readw,
> +&subpage_ram_readl,
> +};
> +
> +static CPUWriteMemoryFunc * const subpage_ram_write[] = {
> +&subpage_ram_writeb,
> +&subpage_ram_writew,
> +&subpage_ram_writel,
> +};
> +
>   static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
>                                ram_addr_t memory, ram_addr_t region_offset)
>   {
> @@ -3583,8 +3640,9 @@ 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;
> +    if ((memory&  ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
> +        memory = IO_MEM_SUBPAGE_RAM;
> +    }
>       memory = (memory>>  IO_MEM_SHIFT)&  (IO_MEM_NB_ENTRIES - 1);
>       for (; idx<= eidx; idx++) {
>           mmio->sub_io_index[idx] = memory;
> @@ -3817,6 +3875,9 @@ static void io_mem_init(void)
>       cpu_register_io_memory_fixed(IO_MEM_NOTDIRTY, error_mem_read,
>                                    notdirty_mem_write, NULL,
>                                    DEVICE_NATIVE_ENDIAN);
> +    cpu_register_io_memory_fixed(IO_MEM_SUBPAGE_RAM, subpage_ram_read,
> +                                 subpage_ram_write, NULL,
> +                                 DEVICE_NATIVE_ENDIAN);
>       for (i=0; i<5; i++)
>           io_mem_used[i] = 1;
>
Andreas Färber Aug. 22, 2012, 4:53 p.m. UTC | #12
Am 15.12.2011 19:09, schrieb Anthony Liguori:
> On 11/30/2011 09:26 AM, 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 change the previous fix to use a new IO_MEM_SUBPAGE_RAM
>> instead of IO_MEM_UNASSIGNED. Suggested by Avi.
>>
>> Signed-off-by: Andreas Färber<afaerber@suse.de>
>> Cc: Avi Kivity<avi@redhat.com>
>> Cc: Gleb Natapov<gleb@redhat.com>
> 
> Applied.  Thanks.

Applied to stable-0.15.

Andreas

> Regards,
> 
> Anthony Liguori
> 
>> ---
>>   cpu-common.h |    1 +
>>   exec.c       |   65
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>>   2 files changed, 64 insertions(+), 2 deletions(-)
>>
>> diff --git a/cpu-common.h b/cpu-common.h
>> index c9878ba..3f45428 100644
>> --- a/cpu-common.h
>> +++ b/cpu-common.h
>> @@ -172,6 +172,7 @@ void
>> cpu_physical_memory_write_rom(target_phys_addr_t addr,
>>   #define IO_MEM_ROM         (1<<  IO_MEM_SHIFT) /* hardcoded offset */
>>   #define IO_MEM_UNASSIGNED  (2<<  IO_MEM_SHIFT)
>>   #define IO_MEM_NOTDIRTY    (3<<  IO_MEM_SHIFT)
>> +#define IO_MEM_SUBPAGE_RAM (4<<  IO_MEM_SHIFT)
>>
>>   /* Acts like a ROM when read and like a device when written.  */
>>   #define IO_MEM_ROMD        (1)
>> diff --git a/exec.c b/exec.c
>> index 6b92198..6c206ff 100644
>> --- a/exec.c
>> +++ b/exec.c
>> @@ -3570,6 +3570,63 @@ static CPUWriteMemoryFunc * const
>> subpage_write[] = {
>>       &subpage_writel,
>>   };
>>
>> +static uint32_t subpage_ram_readb(void *opaque, target_phys_addr_t addr)
>> +{
>> +    ram_addr_t raddr = addr;
>> +    void *ptr = qemu_get_ram_ptr(raddr);
>> +    return ldub_p(ptr);
>> +}
>> +
>> +static void subpage_ram_writeb(void *opaque, target_phys_addr_t addr,
>> +                               uint32_t value)
>> +{
>> +    ram_addr_t raddr = addr;
>> +    void *ptr = qemu_get_ram_ptr(raddr);
>> +    stb_p(ptr, value);
>> +}
>> +
>> +static uint32_t subpage_ram_readw(void *opaque, target_phys_addr_t addr)
>> +{
>> +    ram_addr_t raddr = addr;
>> +    void *ptr = qemu_get_ram_ptr(raddr);
>> +    return lduw_p(ptr);
>> +}
>> +
>> +static void subpage_ram_writew(void *opaque, target_phys_addr_t addr,
>> +                               uint32_t value)
>> +{
>> +    ram_addr_t raddr = addr;
>> +    void *ptr = qemu_get_ram_ptr(raddr);
>> +    stw_p(ptr, value);
>> +}
>> +
>> +static uint32_t subpage_ram_readl(void *opaque, target_phys_addr_t addr)
>> +{
>> +    ram_addr_t raddr = addr;
>> +    void *ptr = qemu_get_ram_ptr(raddr);
>> +    return ldl_p(ptr);
>> +}
>> +
>> +static void subpage_ram_writel(void *opaque, target_phys_addr_t addr,
>> +                               uint32_t value)
>> +{
>> +    ram_addr_t raddr = addr;
>> +    void *ptr = qemu_get_ram_ptr(raddr);
>> +    stl_p(ptr, value);
>> +}
>> +
>> +static CPUReadMemoryFunc * const subpage_ram_read[] = {
>> +&subpage_ram_readb,
>> +&subpage_ram_readw,
>> +&subpage_ram_readl,
>> +};
>> +
>> +static CPUWriteMemoryFunc * const subpage_ram_write[] = {
>> +&subpage_ram_writeb,
>> +&subpage_ram_writew,
>> +&subpage_ram_writel,
>> +};
>> +
>>   static int subpage_register (subpage_t *mmio, uint32_t start,
>> uint32_t end,
>>                                ram_addr_t memory, ram_addr_t
>> region_offset)
>>   {
>> @@ -3583,8 +3640,9 @@ 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;
>> +    if ((memory&  ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
>> +        memory = IO_MEM_SUBPAGE_RAM;
>> +    }
>>       memory = (memory>>  IO_MEM_SHIFT)&  (IO_MEM_NB_ENTRIES - 1);
>>       for (; idx<= eidx; idx++) {
>>           mmio->sub_io_index[idx] = memory;
>> @@ -3817,6 +3875,9 @@ static void io_mem_init(void)
>>       cpu_register_io_memory_fixed(IO_MEM_NOTDIRTY, error_mem_read,
>>                                    notdirty_mem_write, NULL,
>>                                    DEVICE_NATIVE_ENDIAN);
>> +    cpu_register_io_memory_fixed(IO_MEM_SUBPAGE_RAM, subpage_ram_read,
>> +                                 subpage_ram_write, NULL,
>> +                                 DEVICE_NATIVE_ENDIAN);
>>       for (i=0; i<5; i++)
>>           io_mem_used[i] = 1;
>>
diff mbox

Patch

diff --git a/cpu-common.h b/cpu-common.h
index c9878ba..3f45428 100644
--- a/cpu-common.h
+++ b/cpu-common.h
@@ -172,6 +172,7 @@  void cpu_physical_memory_write_rom(target_phys_addr_t addr,
 #define IO_MEM_ROM         (1 << IO_MEM_SHIFT) /* hardcoded offset */
 #define IO_MEM_UNASSIGNED  (2 << IO_MEM_SHIFT)
 #define IO_MEM_NOTDIRTY    (3 << IO_MEM_SHIFT)
+#define IO_MEM_SUBPAGE_RAM (4 << IO_MEM_SHIFT)
 
 /* Acts like a ROM when read and like a device when written.  */
 #define IO_MEM_ROMD        (1)
diff --git a/exec.c b/exec.c
index 6b92198..6c206ff 100644
--- a/exec.c
+++ b/exec.c
@@ -3570,6 +3570,63 @@  static CPUWriteMemoryFunc * const subpage_write[] = {
     &subpage_writel,
 };
 
+static uint32_t subpage_ram_readb(void *opaque, target_phys_addr_t addr)
+{
+    ram_addr_t raddr = addr;
+    void *ptr = qemu_get_ram_ptr(raddr);
+    return ldub_p(ptr);
+}
+
+static void subpage_ram_writeb(void *opaque, target_phys_addr_t addr,
+                               uint32_t value)
+{
+    ram_addr_t raddr = addr;
+    void *ptr = qemu_get_ram_ptr(raddr);
+    stb_p(ptr, value);
+}
+
+static uint32_t subpage_ram_readw(void *opaque, target_phys_addr_t addr)
+{
+    ram_addr_t raddr = addr;
+    void *ptr = qemu_get_ram_ptr(raddr);
+    return lduw_p(ptr);
+}
+
+static void subpage_ram_writew(void *opaque, target_phys_addr_t addr,
+                               uint32_t value)
+{
+    ram_addr_t raddr = addr;
+    void *ptr = qemu_get_ram_ptr(raddr);
+    stw_p(ptr, value);
+}
+
+static uint32_t subpage_ram_readl(void *opaque, target_phys_addr_t addr)
+{
+    ram_addr_t raddr = addr;
+    void *ptr = qemu_get_ram_ptr(raddr);
+    return ldl_p(ptr);
+}
+
+static void subpage_ram_writel(void *opaque, target_phys_addr_t addr,
+                               uint32_t value)
+{
+    ram_addr_t raddr = addr;
+    void *ptr = qemu_get_ram_ptr(raddr);
+    stl_p(ptr, value);
+}
+
+static CPUReadMemoryFunc * const subpage_ram_read[] = {
+    &subpage_ram_readb,
+    &subpage_ram_readw,
+    &subpage_ram_readl,
+};
+
+static CPUWriteMemoryFunc * const subpage_ram_write[] = {
+    &subpage_ram_writeb,
+    &subpage_ram_writew,
+    &subpage_ram_writel,
+};
+
 static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
                              ram_addr_t memory, ram_addr_t region_offset)
 {
@@ -3583,8 +3640,9 @@  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;
+    if ((memory & ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
+        memory = IO_MEM_SUBPAGE_RAM;
+    }
     memory = (memory >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
     for (; idx <= eidx; idx++) {
         mmio->sub_io_index[idx] = memory;
@@ -3817,6 +3875,9 @@  static void io_mem_init(void)
     cpu_register_io_memory_fixed(IO_MEM_NOTDIRTY, error_mem_read,
                                  notdirty_mem_write, NULL,
                                  DEVICE_NATIVE_ENDIAN);
+    cpu_register_io_memory_fixed(IO_MEM_SUBPAGE_RAM, subpage_ram_read,
+                                 subpage_ram_write, NULL,
+                                 DEVICE_NATIVE_ENDIAN);
     for (i=0; i<5; i++)
         io_mem_used[i] = 1;