diff mbox series

[4/8] boards.h: Define new flag ignore_memory_transaction_failures

Message ID 1501867249-1924-5-git-send-email-peter.maydell@linaro.org
State Superseded
Headers show
Series Implement ARM external abort handling | expand

Commit Message

Peter Maydell Aug. 4, 2017, 5:20 p.m. UTC
Define a new MachineClass field ignore_memory_transaction_failures.
If this is flag is true then the CPU will ignore memory transaction
failures which should cause the CPU to take an exception due to an
access to an unassigned physical address; the transaction will
instead return zero (for a read) or be ignored (for a write).  This
should be set only by legacy board models which rely on the old
RAZ/WI behaviour for handling devices that QEMU does not yet model.
New board models should instead use "unimplemented-device" for all
memory ranges where the guest will attempt to probe for a device that
QEMU doesn't implement and a stub device is required.

We need this for ARM boards, where we're about to implement support for
generating external aborts on memory transaction failures. Too many
of our legacy board models rely on the RAZ/WI behaviour and we
would break currently working guests when their "probe for device"
code provoked an external abort rather than a RAZ.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

---
 include/hw/boards.h | 11 +++++++++++
 include/qom/cpu.h   |  7 ++++++-
 qom/cpu.c           |  7 +++++++
 3 files changed, 24 insertions(+), 1 deletion(-)

-- 
2.7.4

Comments

Philippe Mathieu-Daudé Aug. 4, 2017, 6:09 p.m. UTC | #1
On 08/04/2017 02:20 PM, Peter Maydell wrote:
> Define a new MachineClass field ignore_memory_transaction_failures.

> If this is flag is true then the CPU will ignore memory transaction

> failures which should cause the CPU to take an exception due to an

> access to an unassigned physical address; the transaction will

> instead return zero (for a read) or be ignored (for a write).  This

> should be set only by legacy board models which rely on the old

> RAZ/WI behaviour for handling devices that QEMU does not yet model.

> New board models should instead use "unimplemented-device" for all

> memory ranges where the guest will attempt to probe for a device that

> QEMU doesn't implement and a stub device is required.


This is a very good idea. At least it will help understanding why not 
all firmwares compiled for the same board can boot.

Since create_unimplemented_device() register overlapped with low 
priority, why not register it as default device directly, over the whole 
address space?

> 

> We need this for ARM boards, where we're about to implement support for

> generating external aborts on memory transaction failures. Too many

> of our legacy board models rely on the RAZ/WI behaviour and we

> would break currently working guests when their "probe for device"

> code provoked an external abort rather than a RAZ.


I think some firmware will give some surprises, those probing device is 
not here and expect RAZ/WI. I remember some fw probing PCI space, or 
enumerating CS this way for ex.

RAZ/WI is a bus-feature, this is also bus-dependent to reply with abort 
or behave RAZ/WI. Maybe the effort should be done on how model/use buses 
in QEMU? Bus device would be an alias of unimplemented_device, which 
current purpose is more debugging than avoiding unassigned physical 
access aborts.

I'm pretty sure this library setup probes for unassigned access 
installing an handler and checking it got hit, in this case (ab)using 
unimplemented_device would prevent this firmware to boot:
http://www.ti.com/ww/en/functional_safety/safeti/index.html
(I might have self-answered my first question)

> 

> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

> ---

>   include/hw/boards.h | 11 +++++++++++

>   include/qom/cpu.h   |  7 ++++++-

>   qom/cpu.c           |  7 +++++++

>   3 files changed, 24 insertions(+), 1 deletion(-)

> 

> diff --git a/include/hw/boards.h b/include/hw/boards.h

> index 3363dd1..7f044d1 100644

> --- a/include/hw/boards.h

> +++ b/include/hw/boards.h

> @@ -131,6 +131,16 @@ typedef struct {

>    *    size than the target architecture's minimum. (Attempting to create

>    *    such a CPU will fail.) Note that changing this is a migration

>    *    compatibility break for the machine.

> + * @ignore_memory_transaction_failures:

> + *    If this is flag is true then the CPU will ignore memory transaction

> + *    failures which should cause the CPU to take an exception due to an

> + *    access to an unassigned physical address; the transaction will instead

> + *    return zero (for a read) or be ignored (for a write). This should be

> + *    set only by legacy board models which rely on the old RAZ/WI behaviour

> + *    for handling devices that QEMU does not yet model. New board models

> + *    should instead use "unimplemented-device" for all memory ranges where

> + *    the guest will attempt to probe for a device that QEMU doesn't

> + *    implement and a stub device is required.

>    */

>   struct MachineClass {

>       /*< private >*/

> @@ -171,6 +181,7 @@ struct MachineClass {

>       bool rom_file_has_mr;

>       int minimum_page_bits;

>       bool has_hotpluggable_cpus;

> +    bool ignore_memory_transaction_failures;

>       int numa_mem_align_shift;

>       void (*numa_auto_assign_ram)(MachineClass *mc, NodeInfo *nodes,

>                                    int nb_nodes, ram_addr_t size);

> diff --git a/include/qom/cpu.h b/include/qom/cpu.h

> index fc54d55..8cff86f 100644

> --- a/include/qom/cpu.h

> +++ b/include/qom/cpu.h

> @@ -311,6 +311,9 @@ struct qemu_work_item;

>    * @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes

>    *                        to @trace_dstate).

>    * @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).

> + * @ignore_memory_transaction_failures: Cached copy of the MachineState

> + *    flag of the same name: allows the board to suppress calling of the

> + *    CPU do_transaction_failed hook function.

>    *

>    * State of one CPU core or thread.

>    */

> @@ -397,6 +400,8 @@ struct CPUState {

>        */

>       bool throttle_thread_scheduled;

>   

> +    bool ignore_memory_transaction_failures;

> +

>       /* Note that this is accessed at the start of every TB via a negative

>          offset from AREG0.  Leave this field at the end so as to make the

>          (absolute value) offset as small as possible.  This reduces code

> @@ -853,7 +858,7 @@ static inline void cpu_transaction_failed(CPUState *cpu, hwaddr physaddr,

>   {

>       CPUClass *cc = CPU_GET_CLASS(cpu);

>   

> -    if (cc->do_transaction_failed) {

> +    if (!cpu->ignore_memory_transaction_failures && cc->do_transaction_failed) {

>           cc->do_transaction_failed(cpu, physaddr, addr, size, access_type,

>                                     mmu_idx, attrs, response, retaddr);

>       }

> diff --git a/qom/cpu.c b/qom/cpu.c

> index 4f38db0..d8dcf64 100644

> --- a/qom/cpu.c

> +++ b/qom/cpu.c

> @@ -29,6 +29,7 @@

>   #include "exec/cpu-common.h"

>   #include "qemu/error-report.h"

>   #include "sysemu/sysemu.h"

> +#include "hw/boards.h"

>   #include "hw/qdev-properties.h"

>   #include "trace-root.h"

>   

> @@ -360,6 +361,12 @@ static void cpu_common_parse_features(const char *typename, char *features,

>   static void cpu_common_realizefn(DeviceState *dev, Error **errp)

>   {

>       CPUState *cpu = CPU(dev);

> +    Object *machine = qdev_get_machine();

> +    ObjectClass *oc = object_get_class(machine);

> +    MachineClass *mc = MACHINE_CLASS(oc);

> +

> +    cpu->ignore_memory_transaction_failures =

> +        mc->ignore_memory_transaction_failures;

>   

>       if (dev->hotplugged) {

>           cpu_synchronize_post_init(cpu);

>
Richard Henderson Aug. 4, 2017, 7:23 p.m. UTC | #2
On 08/04/2017 11:09 AM, Philippe Mathieu-Daudé wrote:
> Since create_unimplemented_device() register overlapped with low priority, why

> not register it as default device directly, over the whole address space?


That's a good suggestion.  It makes more sense to me than adding a flag on the
MachineClass.


r~
Edgar E. Iglesias Aug. 5, 2017, 1:23 a.m. UTC | #3
On Fri, Aug 04, 2017 at 06:20:45PM +0100, Peter Maydell wrote:
> Define a new MachineClass field ignore_memory_transaction_failures.

> If this is flag is true then the CPU will ignore memory transaction

> failures which should cause the CPU to take an exception due to an

> access to an unassigned physical address; the transaction will

> instead return zero (for a read) or be ignored (for a write).  This

> should be set only by legacy board models which rely on the old

> RAZ/WI behaviour for handling devices that QEMU does not yet model.

> New board models should instead use "unimplemented-device" for all

> memory ranges where the guest will attempt to probe for a device that

> QEMU doesn't implement and a stub device is required.

> 

> We need this for ARM boards, where we're about to implement support for

> generating external aborts on memory transaction failures. Too many

> of our legacy board models rely on the RAZ/WI behaviour and we

> would break currently working guests when their "probe for device"

> code provoked an external abort rather than a RAZ.

> 

> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>


Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>



> ---

>  include/hw/boards.h | 11 +++++++++++

>  include/qom/cpu.h   |  7 ++++++-

>  qom/cpu.c           |  7 +++++++

>  3 files changed, 24 insertions(+), 1 deletion(-)

> 

> diff --git a/include/hw/boards.h b/include/hw/boards.h

> index 3363dd1..7f044d1 100644

> --- a/include/hw/boards.h

> +++ b/include/hw/boards.h

> @@ -131,6 +131,16 @@ typedef struct {

>   *    size than the target architecture's minimum. (Attempting to create

>   *    such a CPU will fail.) Note that changing this is a migration

>   *    compatibility break for the machine.

> + * @ignore_memory_transaction_failures:

> + *    If this is flag is true then the CPU will ignore memory transaction

> + *    failures which should cause the CPU to take an exception due to an

> + *    access to an unassigned physical address; the transaction will instead

> + *    return zero (for a read) or be ignored (for a write). This should be

> + *    set only by legacy board models which rely on the old RAZ/WI behaviour

> + *    for handling devices that QEMU does not yet model. New board models

> + *    should instead use "unimplemented-device" for all memory ranges where

> + *    the guest will attempt to probe for a device that QEMU doesn't

> + *    implement and a stub device is required.

>   */

>  struct MachineClass {

>      /*< private >*/

> @@ -171,6 +181,7 @@ struct MachineClass {

>      bool rom_file_has_mr;

>      int minimum_page_bits;

>      bool has_hotpluggable_cpus;

> +    bool ignore_memory_transaction_failures;

>      int numa_mem_align_shift;

>      void (*numa_auto_assign_ram)(MachineClass *mc, NodeInfo *nodes,

>                                   int nb_nodes, ram_addr_t size);

> diff --git a/include/qom/cpu.h b/include/qom/cpu.h

> index fc54d55..8cff86f 100644

> --- a/include/qom/cpu.h

> +++ b/include/qom/cpu.h

> @@ -311,6 +311,9 @@ struct qemu_work_item;

>   * @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes

>   *                        to @trace_dstate).

>   * @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).

> + * @ignore_memory_transaction_failures: Cached copy of the MachineState

> + *    flag of the same name: allows the board to suppress calling of the

> + *    CPU do_transaction_failed hook function.

>   *

>   * State of one CPU core or thread.

>   */

> @@ -397,6 +400,8 @@ struct CPUState {

>       */

>      bool throttle_thread_scheduled;

>  

> +    bool ignore_memory_transaction_failures;

> +

>      /* Note that this is accessed at the start of every TB via a negative

>         offset from AREG0.  Leave this field at the end so as to make the

>         (absolute value) offset as small as possible.  This reduces code

> @@ -853,7 +858,7 @@ static inline void cpu_transaction_failed(CPUState *cpu, hwaddr physaddr,

>  {

>      CPUClass *cc = CPU_GET_CLASS(cpu);

>  

> -    if (cc->do_transaction_failed) {

> +    if (!cpu->ignore_memory_transaction_failures && cc->do_transaction_failed) {

>          cc->do_transaction_failed(cpu, physaddr, addr, size, access_type,

>                                    mmu_idx, attrs, response, retaddr);

>      }

> diff --git a/qom/cpu.c b/qom/cpu.c

> index 4f38db0..d8dcf64 100644

> --- a/qom/cpu.c

> +++ b/qom/cpu.c

> @@ -29,6 +29,7 @@

>  #include "exec/cpu-common.h"

>  #include "qemu/error-report.h"

>  #include "sysemu/sysemu.h"

> +#include "hw/boards.h"

>  #include "hw/qdev-properties.h"

>  #include "trace-root.h"

>  

> @@ -360,6 +361,12 @@ static void cpu_common_parse_features(const char *typename, char *features,

>  static void cpu_common_realizefn(DeviceState *dev, Error **errp)

>  {

>      CPUState *cpu = CPU(dev);

> +    Object *machine = qdev_get_machine();

> +    ObjectClass *oc = object_get_class(machine);

> +    MachineClass *mc = MACHINE_CLASS(oc);

> +

> +    cpu->ignore_memory_transaction_failures =

> +        mc->ignore_memory_transaction_failures;

>  

>      if (dev->hotplugged) {

>          cpu_synchronize_post_init(cpu);

> -- 

> 2.7.4

> 

>
Peter Maydell Aug. 5, 2017, 10:13 a.m. UTC | #4
On 4 August 2017 at 20:23, Richard Henderson
<richard.henderson@linaro.org> wrote:
> On 08/04/2017 11:09 AM, Philippe Mathieu-Daudé wrote:

>> Since create_unimplemented_device() register overlapped with low priority, why

>> not register it as default device directly, over the whole address space?

>

> That's a good suggestion.  It makes more sense to me than adding a flag on the

> MachineClass.


Yeah, I did think about implementing it that way, but...

That wouldn't handle the case of a device model directly
returning a MEMTX_ERROR, or a transaction dispatched to
a memory region whose MemoryRegionOps valid settings
prohibit it (eg byte accesses to a word-access-only device),
or accesses to a MemoryRegion that was created by passing
a NULL MemoryRegionOps pointer to memory_region_init_io
(I dunno why you'd do that but some code does).

In short, there are lots of ways the memory subsystem might
end up returning a transaction error -- this mechanism
ensures that none of them start generating exceptions
when they previously did not, and is (I hope) easy to
review in the sense of being sure that it does what it
intends to do without the need to audit a lot of corner
cases.

thanks
-- PMM
Peter Maydell Aug. 5, 2017, 10:29 a.m. UTC | #5
On 4 August 2017 at 19:09, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> On 08/04/2017 02:20 PM, Peter Maydell wrote:

>> We need this for ARM boards, where we're about to implement support for

>> generating external aborts on memory transaction failures. Too many

>> of our legacy board models rely on the RAZ/WI behaviour and we

>> would break currently working guests when their "probe for device"

>> code provoked an external abort rather than a RAZ.


> I think some firmware will give some surprises, those probing device is not

> here and expect RAZ/WI. I remember some fw probing PCI space, or enumerating

> CS this way for ex.


PCI space is funny anyway because IIRC the PCI spec mandates
RAZ/WI (which is handled by QEMU's PCI implementation I think,
it doesn't fall out to the memory system's unmapped-address
handling).

That said: yes, possibly some guest code really wants the fault
(indeed the motivation for this patchset was having some test
guest code which wanted to see the faults), but that guest code
won't work on QEMU today, so if it doesn't boot on QEMU with
this patchsets that's not a regression. We can then (as the
issue arises) look at fixing whatever particular board model
it is to properly model or stub out all its devices so we
can boot that guest code without breaking existing working
guest code.

> RAZ/WI is a bus-feature, this is also bus-dependent to reply with abort or

> behave RAZ/WI. Maybe the effort should be done on how model/use buses in

> QEMU? Bus device would be an alias of unimplemented_device, which current

> purpose is more debugging than avoiding unassigned physical access aborts.


You can model this kind of bus-dependent behaviour by having
the bus register a background region which implements the
default behaviour that is desired. (That way accesses to
that part of the address space don't ever respond with
a transaction error, which is what's happening on hardware
where the bus doesn't report errors.)

thanks
-- PMM
Peter Maydell Aug. 17, 2017, 10:25 a.m. UTC | #6
On 5 August 2017 at 11:13, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 4 August 2017 at 20:23, Richard Henderson

> <richard.henderson@linaro.org> wrote:

>> On 08/04/2017 11:09 AM, Philippe Mathieu-Daudé wrote:

>>> Since create_unimplemented_device() register overlapped with low priority, why

>>> not register it as default device directly, over the whole address space?

>>

>> That's a good suggestion.  It makes more sense to me than adding a flag on the

>> MachineClass.

>

> Yeah, I did think about implementing it that way, but...

>

> That wouldn't handle the case of a device model directly

> returning a MEMTX_ERROR, or a transaction dispatched to

> a memory region whose MemoryRegionOps valid settings

> prohibit it (eg byte accesses to a word-access-only device),

> or accesses to a MemoryRegion that was created by passing

> a NULL MemoryRegionOps pointer to memory_region_init_io

> (I dunno why you'd do that but some code does).

>

> In short, there are lots of ways the memory subsystem might

> end up returning a transaction error -- this mechanism

> ensures that none of them start generating exceptions

> when they previously did not, and is (I hope) easy to

> review in the sense of being sure that it does what it

> intends to do without the need to audit a lot of corner

> cases.


So, this question (should we have a board flag to disable reporting
of tx failures to the CPU hook, or use unimplemented_device as a
sort of background region) seems to be the main unanswered question
for this series. I think (as outlined above) that the board flag
is simpler and safer; are people happy for me to put this series
in target-arm.next with that approach, or should I rethink this bit?

thanks
-- PMM
Philippe Mathieu-Daudé Aug. 22, 2017, 3:45 a.m. UTC | #7
Hi Peter,

On 08/17/2017 07:25 AM, Peter Maydell wrote:
> On 5 August 2017 at 11:13, Peter Maydell <peter.maydell@linaro.org> wrote:

>> On 4 August 2017 at 20:23, Richard Henderson

>> <richard.henderson@linaro.org> wrote:

>>> On 08/04/2017 11:09 AM, Philippe Mathieu-Daudé wrote:

>>>> Since create_unimplemented_device() register overlapped with low priority, why

>>>> not register it as default device directly, over the whole address space?

>>>

>>> That's a good suggestion.  It makes more sense to me than adding a flag on the

>>> MachineClass.

>>

>> Yeah, I did think about implementing it that way, but...

>>

>> That wouldn't handle the case of a device model directly

>> returning a MEMTX_ERROR, or a transaction dispatched to

>> a memory region whose MemoryRegionOps valid settings

>> prohibit it (eg byte accesses to a word-access-only device),

>> or accesses to a MemoryRegion that was created by passing

>> a NULL MemoryRegionOps pointer to memory_region_init_io

>> (I dunno why you'd do that but some code does).

>>

>> In short, there are lots of ways the memory subsystem might

>> end up returning a transaction error -- this mechanism

>> ensures that none of them start generating exceptions

>> when they previously did not, and is (I hope) easy to

>> review in the sense of being sure that it does what it

>> intends to do without the need to audit a lot of corner

>> cases.

> 

> So, this question (should we have a board flag to disable reporting

> of tx failures to the CPU hook, or use unimplemented_device as a

> sort of background region) seems to be the main unanswered question

> for this series. I think (as outlined above) that the board flag

> is simpler and safer; are people happy for me to put this series

> in target-arm.next with that approach, or should I rethink this bit?

As remarked previously in this thread, the current QEMU behavior on 
transaction error isn't always matching real hardware.
Matching correctly throwing errors is likely to break various current users.

If we are worried about being backward compatible, defaulting background 
region to unimp() won't throw any transaction error.

Since the default is no transaction error, users who expect hardware 
error can implement the correct behavior, per region/bus transaction errors.

I'm somehow afraid that "ignore_memory_transaction_failures" ends up 
like the "cannot_instantiate_with_device_add_yet" flag - a hard to 
remove kludge outliving his purpose.

Anyway I'm not unhappy with this approach, but I'd be very happy to have 
unimp() covering the whole background region.

Regards,

Phil.
Peter Maydell Aug. 22, 2017, 8:36 a.m. UTC | #8
On 22 August 2017 at 04:45, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> On 08/17/2017 07:25 AM, Peter Maydell wrote:

>>

>> On 5 August 2017 at 11:13, Peter Maydell <peter.maydell@linaro.org> wrote:

>> So, this question (should we have a board flag to disable reporting

>> of tx failures to the CPU hook, or use unimplemented_device as a

>> sort of background region) seems to be the main unanswered question

>> for this series. I think (as outlined above) that the board flag

>> is simpler and safer; are people happy for me to put this series

>> in target-arm.next with that approach, or should I rethink this bit?

>

> As remarked previously in this thread, the current QEMU behavior on

> transaction error isn't always matching real hardware.

> Matching correctly throwing errors is likely to break various

> current users.


Yes, hence this patchseries keeping the wrong but back compatible
behaviour...

> If we are worried about being backward compatible, defaulting background

> region to unimp() won't throw any transaction error.


As I said, it will, for the cases of device model directly
returning a MEMTX_ERROR, or a transaction dispatched to
a memory region whose MemoryRegionOps valid settings
prohibit it (eg byte accesses to a word-access-only device), etc.
The only simple way to guarantee that we don't generate exceptions
on transaction errors is to cause the hook not to be called
(or to have the hook decide to do nothing, I suppose).

> I'm somehow afraid that "ignore_memory_transaction_failures" ends up like

> the "cannot_instantiate_with_device_add_yet" flag - a hard to remove kludge

> outliving his purpose.


I agree that it's going to be around for a long time, possibly
forever, but that's life when we have so many old boards.
Any approach we take is almost certainly going to be hanging
around forever.

> Anyway I'm not unhappy with this approach, but I'd be very happy to have

> unimp() covering the whole background region.


I think this would be a reasonable approach for converting
boards away from this ignore_memory_transaction_failures hook
on a board-by-board basis but you'd still want to test some
common guest software for each conversion.

thanks
-- PMM
Richard Henderson Aug. 24, 2017, 8:28 p.m. UTC | #9
On 22 August 2017 at 09:36, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 22 August 2017 at 04:45, Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:

>> If we are worried about being backward compatible, defaulting background

>> region to unimp() won't throw any transaction error.

>

> As I said, it will, for the cases of device model directly

> returning a MEMTX_ERROR, or a transaction dispatched to

> a memory region whose MemoryRegionOps valid settings

> prohibit it (eg byte accesses to a word-access-only device), etc.

> The only simple way to guarantee that we don't generate exceptions

> on transaction errors is to cause the hook not to be called

> (or to have the hook decide to do nothing, I suppose).

>

>> I'm somehow afraid that "ignore_memory_transaction_failures" ends up like

>> the "cannot_instantiate_with_device_add_yet" flag - a hard to remove kludge

>> outliving his purpose.

>

> I agree that it's going to be around for a long time, possibly

> forever, but that's life when we have so many old boards.

> Any approach we take is almost certainly going to be hanging

> around forever.

>

>> Anyway I'm not unhappy with this approach, but I'd be very happy to have

>> unimp() covering the whole background region.

>

> I think this would be a reasonable approach for converting

> boards away from this ignore_memory_transaction_failures hook

> on a board-by-board basis but you'd still want to test some

> common guest software for each conversion.


Peter and I had a chat on IRC about this.

While I think the background region idea is tempting, it does nothing to help
legacy boards once devices start signaling their own errors.  Therefore some
sort of flag is the only reasonable solution.


r~
Peter Maydell Aug. 25, 2017, 12:02 p.m. UTC | #10
On 24 August 2017 at 21:28, Richard Henderson
<richard.henderson@linaro.org> wrote:
> Peter and I had a chat on IRC about this.

>

> While I think the background region idea is tempting, it does nothing to help

> legacy boards once devices start signaling their own errors.  Therefore some

> sort of flag is the only reasonable solution.


Thanks; I'm going to put this series into target-arm.next as-is
(with the couple of minor comment tweaks suggested in review
applied to it), ready for the post-2.10-release pull request.

-- PMM
diff mbox series

Patch

diff --git a/include/hw/boards.h b/include/hw/boards.h
index 3363dd1..7f044d1 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -131,6 +131,16 @@  typedef struct {
  *    size than the target architecture's minimum. (Attempting to create
  *    such a CPU will fail.) Note that changing this is a migration
  *    compatibility break for the machine.
+ * @ignore_memory_transaction_failures:
+ *    If this is flag is true then the CPU will ignore memory transaction
+ *    failures which should cause the CPU to take an exception due to an
+ *    access to an unassigned physical address; the transaction will instead
+ *    return zero (for a read) or be ignored (for a write). This should be
+ *    set only by legacy board models which rely on the old RAZ/WI behaviour
+ *    for handling devices that QEMU does not yet model. New board models
+ *    should instead use "unimplemented-device" for all memory ranges where
+ *    the guest will attempt to probe for a device that QEMU doesn't
+ *    implement and a stub device is required.
  */
 struct MachineClass {
     /*< private >*/
@@ -171,6 +181,7 @@  struct MachineClass {
     bool rom_file_has_mr;
     int minimum_page_bits;
     bool has_hotpluggable_cpus;
+    bool ignore_memory_transaction_failures;
     int numa_mem_align_shift;
     void (*numa_auto_assign_ram)(MachineClass *mc, NodeInfo *nodes,
                                  int nb_nodes, ram_addr_t size);
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index fc54d55..8cff86f 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -311,6 +311,9 @@  struct qemu_work_item;
  * @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes
  *                        to @trace_dstate).
  * @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).
+ * @ignore_memory_transaction_failures: Cached copy of the MachineState
+ *    flag of the same name: allows the board to suppress calling of the
+ *    CPU do_transaction_failed hook function.
  *
  * State of one CPU core or thread.
  */
@@ -397,6 +400,8 @@  struct CPUState {
      */
     bool throttle_thread_scheduled;
 
+    bool ignore_memory_transaction_failures;
+
     /* Note that this is accessed at the start of every TB via a negative
        offset from AREG0.  Leave this field at the end so as to make the
        (absolute value) offset as small as possible.  This reduces code
@@ -853,7 +858,7 @@  static inline void cpu_transaction_failed(CPUState *cpu, hwaddr physaddr,
 {
     CPUClass *cc = CPU_GET_CLASS(cpu);
 
-    if (cc->do_transaction_failed) {
+    if (!cpu->ignore_memory_transaction_failures && cc->do_transaction_failed) {
         cc->do_transaction_failed(cpu, physaddr, addr, size, access_type,
                                   mmu_idx, attrs, response, retaddr);
     }
diff --git a/qom/cpu.c b/qom/cpu.c
index 4f38db0..d8dcf64 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -29,6 +29,7 @@ 
 #include "exec/cpu-common.h"
 #include "qemu/error-report.h"
 #include "sysemu/sysemu.h"
+#include "hw/boards.h"
 #include "hw/qdev-properties.h"
 #include "trace-root.h"
 
@@ -360,6 +361,12 @@  static void cpu_common_parse_features(const char *typename, char *features,
 static void cpu_common_realizefn(DeviceState *dev, Error **errp)
 {
     CPUState *cpu = CPU(dev);
+    Object *machine = qdev_get_machine();
+    ObjectClass *oc = object_get_class(machine);
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    cpu->ignore_memory_transaction_failures =
+        mc->ignore_memory_transaction_failures;
 
     if (dev->hotplugged) {
         cpu_synchronize_post_init(cpu);