diff mbox series

[kvm-unit-tests,14/32] powerpc: general interrupt tests

Message ID 20240226101218.1472843-15-npiggin@gmail.com (mailing list archive)
State New, archived
Headers show
Series powerpc improvements | expand

Commit Message

Nicholas Piggin Feb. 26, 2024, 10:12 a.m. UTC
Add basic testing of various kinds of interrupts, machine check,
page fault, illegal, decrementer, trace, syscall, etc.

This has a known failure on QEMU TCG pseries machines where MSR[ME]
can be incorrectly set to 0.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/processor.h |   4 +
 lib/powerpc/asm/reg.h       |  17 ++
 lib/powerpc/setup.c         |  11 +
 lib/ppc64/asm/ptrace.h      |  16 ++
 powerpc/Makefile.common     |   3 +-
 powerpc/interrupts.c        | 415 ++++++++++++++++++++++++++++++++++++
 powerpc/unittests.cfg       |   3 +
 7 files changed, 468 insertions(+), 1 deletion(-)
 create mode 100644 powerpc/interrupts.c

Comments

Thomas Huth March 1, 2024, 12:41 p.m. UTC | #1
On 26/02/2024 11.12, Nicholas Piggin wrote:
> Add basic testing of various kinds of interrupts, machine check,
> page fault, illegal, decrementer, trace, syscall, etc.
> 
> This has a known failure on QEMU TCG pseries machines where MSR[ME]
> can be incorrectly set to 0.

Two questions out of curiosity:

Any chance that this could be fixed easily in QEMU?

Or is there a way to detect TCG from within the test? (for example, we have 
a host_is_tcg() function for s390x so we can e.g. use report_xfail() for 
tests that are known to fail on TCG there)

> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   lib/powerpc/asm/processor.h |   4 +
>   lib/powerpc/asm/reg.h       |  17 ++
>   lib/powerpc/setup.c         |  11 +
>   lib/ppc64/asm/ptrace.h      |  16 ++
>   powerpc/Makefile.common     |   3 +-
>   powerpc/interrupts.c        | 415 ++++++++++++++++++++++++++++++++++++
>   powerpc/unittests.cfg       |   3 +
>   7 files changed, 468 insertions(+), 1 deletion(-)
>   create mode 100644 powerpc/interrupts.c
> 
> diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
> index cf1b9d8ff..eed37d1f4 100644
> --- a/lib/powerpc/asm/processor.h
> +++ b/lib/powerpc/asm/processor.h
> @@ -11,7 +11,11 @@ void do_handle_exception(struct pt_regs *regs);
>   #endif /* __ASSEMBLY__ */
>   
>   extern bool cpu_has_hv;
> +extern bool cpu_has_power_mce;
> +extern bool cpu_has_siar;
>   extern bool cpu_has_heai;
> +extern bool cpu_has_prefix;
> +extern bool cpu_has_sc_lev;
>   
>   static inline uint64_t mfspr(int nr)
>   {
> diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
> index 782e75527..d6097f48f 100644
> --- a/lib/powerpc/asm/reg.h
> +++ b/lib/powerpc/asm/reg.h
> @@ -5,8 +5,15 @@
>   
>   #define UL(x) _AC(x, UL)
>   
> +#define SPR_DSISR	0x012
> +#define SPR_DAR		0x013
> +#define SPR_DEC		0x016
>   #define SPR_SRR0	0x01a
>   #define SPR_SRR1	0x01b
> +#define   SRR1_PREFIX		UL(0x20000000)
> +#define SPR_FSCR	0x099
> +#define   FSCR_PREFIX		UL(0x2000)
> +#define SPR_HFSCR	0x0be
>   #define SPR_TB		0x10c
>   #define SPR_SPRG0	0x110
>   #define SPR_SPRG1	0x111
> @@ -22,12 +29,17 @@
>   #define   PVR_VER_POWER8	UL(0x004d0000)
>   #define   PVR_VER_POWER9	UL(0x004e0000)
>   #define   PVR_VER_POWER10	UL(0x00800000)
> +#define SPR_HDEC	0x136
>   #define SPR_HSRR0	0x13a
>   #define SPR_HSRR1	0x13b
> +#define SPR_LPCR	0x13e
> +#define   LPCR_HDICE		UL(0x1)
> +#define SPR_HEIR	0x153
>   #define SPR_MMCR0	0x31b
>   #define   MMCR0_FC		UL(0x80000000)
>   #define   MMCR0_PMAE		UL(0x04000000)
>   #define   MMCR0_PMAO		UL(0x00000080)
> +#define SPR_SIAR	0x31c
>   
>   /* Machine State Register definitions: */
>   #define MSR_LE_BIT	0
> @@ -35,6 +47,11 @@
>   #define MSR_HV_BIT	60			/* Hypervisor mode */
>   #define MSR_SF_BIT	63			/* 64-bit mode */
>   
> +#define MSR_DR		UL(0x0010)
> +#define MSR_IR		UL(0x0020)
> +#define MSR_BE		UL(0x0200)		/* Branch Trace Enable */
> +#define MSR_SE		UL(0x0400)		/* Single Step Enable */
> +#define MSR_EE		UL(0x8000)
>   #define MSR_ME		UL(0x1000)
>   
>   #endif
> diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
> index 3c81aee9e..9b665f59c 100644
> --- a/lib/powerpc/setup.c
> +++ b/lib/powerpc/setup.c
> @@ -87,7 +87,11 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
>   }
>   
>   bool cpu_has_hv;
> +bool cpu_has_power_mce; /* POWER CPU machine checks */
> +bool cpu_has_siar;
>   bool cpu_has_heai;
> +bool cpu_has_prefix;
> +bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
>   
>   static void cpu_init(void)
>   {
> @@ -112,15 +116,22 @@ static void cpu_init(void)
>   
>   	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
>   	case PVR_VER_POWER10:
> +		cpu_has_prefix = true;
> +		cpu_has_sc_lev = true;
>   	case PVR_VER_POWER9:
>   	case PVR_VER_POWER8E:
>   	case PVR_VER_POWER8NVL:
>   	case PVR_VER_POWER8:
> +		cpu_has_power_mce = true;
>   		cpu_has_heai = true;
> +		cpu_has_siar = true;
>   		break;
>   	default:
>   		break;
>   	}
> +
> +	if (!cpu_has_hv) /* HEIR is HV register */
> +		cpu_has_heai = false;
>   }
>   
>   static void mem_init(phys_addr_t freemem_start)
> diff --git a/lib/ppc64/asm/ptrace.h b/lib/ppc64/asm/ptrace.h
> index 12de7499b..db263a59e 100644
> --- a/lib/ppc64/asm/ptrace.h
> +++ b/lib/ppc64/asm/ptrace.h
> @@ -5,6 +5,9 @@
>   #define STACK_FRAME_OVERHEAD    112     /* size of minimum stack frame */
>   
>   #ifndef __ASSEMBLY__
> +
> +#include <asm/reg.h>
> +
>   struct pt_regs {
>   	unsigned long gpr[32];
>   	unsigned long nip;
> @@ -17,6 +20,19 @@ struct pt_regs {
>   	unsigned long _pad; /* stack must be 16-byte aligned */
>   };
>   
> +static inline bool regs_is_prefix(volatile struct pt_regs *regs)
> +{
> +	return regs->msr & SRR1_PREFIX;
> +}
> +
> +static inline void regs_advance_insn(struct pt_regs *regs)
> +{
> +	if (regs_is_prefix(regs))
> +		regs->nip += 8;
> +	else
> +		regs->nip += 4;
> +}
> +
>   #define STACK_INT_FRAME_SIZE    (sizeof(struct pt_regs) + \
>   				 STACK_FRAME_OVERHEAD + KERNEL_REDZONE_SIZE)
>   
> diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
> index 1e181da69..68165fc25 100644
> --- a/powerpc/Makefile.common
> +++ b/powerpc/Makefile.common
> @@ -12,7 +12,8 @@ tests-common = \
>   	$(TEST_DIR)/rtas.elf \
>   	$(TEST_DIR)/emulator.elf \
>   	$(TEST_DIR)/tm.elf \
> -	$(TEST_DIR)/sprs.elf
> +	$(TEST_DIR)/sprs.elf \
> +	$(TEST_DIR)/interrupts.elf
>   
>   tests-all = $(tests-common) $(tests)
>   all: directories $(TEST_DIR)/boot_rom.bin $(tests-all)
> diff --git a/powerpc/interrupts.c b/powerpc/interrupts.c
> new file mode 100644
> index 000000000..442f8c569
> --- /dev/null
> +++ b/powerpc/interrupts.c
> @@ -0,0 +1,415 @@
> +/*
> + * Test interrupts
> + *
> + * Copyright 2024 Nicholas Piggin, IBM Corp.
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.

I know, we're using this line in a lot of source files ... but maybe we 
should do better for new files at least: "LGPL, version 2" is a little bit 
ambiguous: Does it mean the "Library GPL version 2.0" or the "Lesser GPL 
version 2.1"? Maybe you could clarify by additionally providing a SPDX 
identifier here, or by explicitly writing 2.0 or 2.1.

  Thanks,
   Thomas
Andrew Jones March 1, 2024, 1:45 p.m. UTC | #2
On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
> On 26/02/2024 11.12, Nicholas Piggin wrote:
> > Add basic testing of various kinds of interrupts, machine check,
> > page fault, illegal, decrementer, trace, syscall, etc.
> > 
> > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > can be incorrectly set to 0.
> 
> Two questions out of curiosity:
> 
> Any chance that this could be fixed easily in QEMU?
> 
> Or is there a way to detect TCG from within the test? (for example, we have
> a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
> tests that are known to fail on TCG there)

If there's nothing better, then it should be possible to check the
QEMU_ACCEL environment variable which will be there with the default
environ.

> 
> > @@ -0,0 +1,415 @@
> > +/*
> > + * Test interrupts
> > + *
> > + * Copyright 2024 Nicholas Piggin, IBM Corp.
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.
> 
> I know, we're using this line in a lot of source files ... but maybe we
> should do better for new files at least: "LGPL, version 2" is a little bit
> ambiguous: Does it mean the "Library GPL version 2.0" or the "Lesser GPL
> version 2.1"? Maybe you could clarify by additionally providing a SPDX
> identifier here, or by explicitly writing 2.0 or 2.1.

Let's only add SPDX identifiers to new files.

Thanks,
drew
Thomas Huth March 1, 2024, 1:57 p.m. UTC | #3
On 01/03/2024 14.45, Andrew Jones wrote:
> On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
>> On 26/02/2024 11.12, Nicholas Piggin wrote:
>>> Add basic testing of various kinds of interrupts, machine check,
>>> page fault, illegal, decrementer, trace, syscall, etc.
>>>
>>> This has a known failure on QEMU TCG pseries machines where MSR[ME]
>>> can be incorrectly set to 0.
>>
>> Two questions out of curiosity:
>>
>> Any chance that this could be fixed easily in QEMU?
>>
>> Or is there a way to detect TCG from within the test? (for example, we have
>> a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
>> tests that are known to fail on TCG there)
> 
> If there's nothing better, then it should be possible to check the
> QEMU_ACCEL environment variable which will be there with the default
> environ.

Well, but that's only available from the host side, not within the test 
(i.e. the guest). So that does not help much with report_xfail...
I was rather thinking of something like checking the device tree, e.g. for 
the compatible property in /hypervisor to see whether it's KVM or TCG...?

  Thomas
Andrew Jones March 1, 2024, 2:14 p.m. UTC | #4
On Fri, Mar 01, 2024 at 02:57:04PM +0100, Thomas Huth wrote:
> On 01/03/2024 14.45, Andrew Jones wrote:
> > On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
> > > On 26/02/2024 11.12, Nicholas Piggin wrote:
> > > > Add basic testing of various kinds of interrupts, machine check,
> > > > page fault, illegal, decrementer, trace, syscall, etc.
> > > > 
> > > > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > > > can be incorrectly set to 0.
> > > 
> > > Two questions out of curiosity:
> > > 
> > > Any chance that this could be fixed easily in QEMU?
> > > 
> > > Or is there a way to detect TCG from within the test? (for example, we have
> > > a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
> > > tests that are known to fail on TCG there)
> > 
> > If there's nothing better, then it should be possible to check the
> > QEMU_ACCEL environment variable which will be there with the default
> > environ.
> 
> Well, but that's only available from the host side, not within the test
> (i.e. the guest). So that does not help much with report_xfail...

powerpc has had environment variables in guests since commit f266c3e8ef15
("powerpc: enable environ"). QEMU_ACCEL is one of the environment
variables given to unit tests by default when ENVIRON_DEFAULT is 'yes', as
is the default set in configure. But...

> I was rather thinking of something like checking the device tree, e.g. for
> the compatible property in /hypervisor to see whether it's KVM or TCG...?

...while QEMU_ACCEL will work when the environ is present, DT will always
be present, so checking the hypervisor node sounds better to me.

Thanks,
drew
Nicholas Piggin March 5, 2024, 2:19 a.m. UTC | #5
On Fri Mar 1, 2024 at 10:41 PM AEST, Thomas Huth wrote:
> On 26/02/2024 11.12, Nicholas Piggin wrote:
> > Add basic testing of various kinds of interrupts, machine check,
> > page fault, illegal, decrementer, trace, syscall, etc.
> > 
> > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > can be incorrectly set to 0.
>
> Two questions out of curiosity:
>
> Any chance that this could be fixed easily in QEMU?

Yes I have a fix on the mailing list. It should get into 9.0 and
probably stable.

> Or is there a way to detect TCG from within the test? (for example, we have 
> a host_is_tcg() function for s390x so we can e.g. use report_xfail() for 
> tests that are known to fail on TCG there)

I do have a half-done patch which adds exactly this.

One (minor) annoyance is that it doesn't seem possible to detect QEMU
version to add workarounds. E.g., we would like to test the fixed
functionality, but older qemu should not. Maybe that's going too much
into a rabbit hole. We *could* put a QEMU version into device tree
to deal with this though...

Thanks,
Nick
>
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > ---
> >   lib/powerpc/asm/processor.h |   4 +
> >   lib/powerpc/asm/reg.h       |  17 ++
> >   lib/powerpc/setup.c         |  11 +
> >   lib/ppc64/asm/ptrace.h      |  16 ++
> >   powerpc/Makefile.common     |   3 +-
> >   powerpc/interrupts.c        | 415 ++++++++++++++++++++++++++++++++++++
> >   powerpc/unittests.cfg       |   3 +
> >   7 files changed, 468 insertions(+), 1 deletion(-)
> >   create mode 100644 powerpc/interrupts.c
> > 
> > diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
> > index cf1b9d8ff..eed37d1f4 100644
> > --- a/lib/powerpc/asm/processor.h
> > +++ b/lib/powerpc/asm/processor.h
> > @@ -11,7 +11,11 @@ void do_handle_exception(struct pt_regs *regs);
> >   #endif /* __ASSEMBLY__ */
> >   
> >   extern bool cpu_has_hv;
> > +extern bool cpu_has_power_mce;
> > +extern bool cpu_has_siar;
> >   extern bool cpu_has_heai;
> > +extern bool cpu_has_prefix;
> > +extern bool cpu_has_sc_lev;
> >   
> >   static inline uint64_t mfspr(int nr)
> >   {
> > diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
> > index 782e75527..d6097f48f 100644
> > --- a/lib/powerpc/asm/reg.h
> > +++ b/lib/powerpc/asm/reg.h
> > @@ -5,8 +5,15 @@
> >   
> >   #define UL(x) _AC(x, UL)
> >   
> > +#define SPR_DSISR	0x012
> > +#define SPR_DAR		0x013
> > +#define SPR_DEC		0x016
> >   #define SPR_SRR0	0x01a
> >   #define SPR_SRR1	0x01b
> > +#define   SRR1_PREFIX		UL(0x20000000)
> > +#define SPR_FSCR	0x099
> > +#define   FSCR_PREFIX		UL(0x2000)
> > +#define SPR_HFSCR	0x0be
> >   #define SPR_TB		0x10c
> >   #define SPR_SPRG0	0x110
> >   #define SPR_SPRG1	0x111
> > @@ -22,12 +29,17 @@
> >   #define   PVR_VER_POWER8	UL(0x004d0000)
> >   #define   PVR_VER_POWER9	UL(0x004e0000)
> >   #define   PVR_VER_POWER10	UL(0x00800000)
> > +#define SPR_HDEC	0x136
> >   #define SPR_HSRR0	0x13a
> >   #define SPR_HSRR1	0x13b
> > +#define SPR_LPCR	0x13e
> > +#define   LPCR_HDICE		UL(0x1)
> > +#define SPR_HEIR	0x153
> >   #define SPR_MMCR0	0x31b
> >   #define   MMCR0_FC		UL(0x80000000)
> >   #define   MMCR0_PMAE		UL(0x04000000)
> >   #define   MMCR0_PMAO		UL(0x00000080)
> > +#define SPR_SIAR	0x31c
> >   
> >   /* Machine State Register definitions: */
> >   #define MSR_LE_BIT	0
> > @@ -35,6 +47,11 @@
> >   #define MSR_HV_BIT	60			/* Hypervisor mode */
> >   #define MSR_SF_BIT	63			/* 64-bit mode */
> >   
> > +#define MSR_DR		UL(0x0010)
> > +#define MSR_IR		UL(0x0020)
> > +#define MSR_BE		UL(0x0200)		/* Branch Trace Enable */
> > +#define MSR_SE		UL(0x0400)		/* Single Step Enable */
> > +#define MSR_EE		UL(0x8000)
> >   #define MSR_ME		UL(0x1000)
> >   
> >   #endif
> > diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
> > index 3c81aee9e..9b665f59c 100644
> > --- a/lib/powerpc/setup.c
> > +++ b/lib/powerpc/setup.c
> > @@ -87,7 +87,11 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
> >   }
> >   
> >   bool cpu_has_hv;
> > +bool cpu_has_power_mce; /* POWER CPU machine checks */
> > +bool cpu_has_siar;
> >   bool cpu_has_heai;
> > +bool cpu_has_prefix;
> > +bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
> >   
> >   static void cpu_init(void)
> >   {
> > @@ -112,15 +116,22 @@ static void cpu_init(void)
> >   
> >   	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
> >   	case PVR_VER_POWER10:
> > +		cpu_has_prefix = true;
> > +		cpu_has_sc_lev = true;
> >   	case PVR_VER_POWER9:
> >   	case PVR_VER_POWER8E:
> >   	case PVR_VER_POWER8NVL:
> >   	case PVR_VER_POWER8:
> > +		cpu_has_power_mce = true;
> >   		cpu_has_heai = true;
> > +		cpu_has_siar = true;
> >   		break;
> >   	default:
> >   		break;
> >   	}
> > +
> > +	if (!cpu_has_hv) /* HEIR is HV register */
> > +		cpu_has_heai = false;
> >   }
> >   
> >   static void mem_init(phys_addr_t freemem_start)
> > diff --git a/lib/ppc64/asm/ptrace.h b/lib/ppc64/asm/ptrace.h
> > index 12de7499b..db263a59e 100644
> > --- a/lib/ppc64/asm/ptrace.h
> > +++ b/lib/ppc64/asm/ptrace.h
> > @@ -5,6 +5,9 @@
> >   #define STACK_FRAME_OVERHEAD    112     /* size of minimum stack frame */
> >   
> >   #ifndef __ASSEMBLY__
> > +
> > +#include <asm/reg.h>
> > +
> >   struct pt_regs {
> >   	unsigned long gpr[32];
> >   	unsigned long nip;
> > @@ -17,6 +20,19 @@ struct pt_regs {
> >   	unsigned long _pad; /* stack must be 16-byte aligned */
> >   };
> >   
> > +static inline bool regs_is_prefix(volatile struct pt_regs *regs)
> > +{
> > +	return regs->msr & SRR1_PREFIX;
> > +}
> > +
> > +static inline void regs_advance_insn(struct pt_regs *regs)
> > +{
> > +	if (regs_is_prefix(regs))
> > +		regs->nip += 8;
> > +	else
> > +		regs->nip += 4;
> > +}
> > +
> >   #define STACK_INT_FRAME_SIZE    (sizeof(struct pt_regs) + \
> >   				 STACK_FRAME_OVERHEAD + KERNEL_REDZONE_SIZE)
> >   
> > diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
> > index 1e181da69..68165fc25 100644
> > --- a/powerpc/Makefile.common
> > +++ b/powerpc/Makefile.common
> > @@ -12,7 +12,8 @@ tests-common = \
> >   	$(TEST_DIR)/rtas.elf \
> >   	$(TEST_DIR)/emulator.elf \
> >   	$(TEST_DIR)/tm.elf \
> > -	$(TEST_DIR)/sprs.elf
> > +	$(TEST_DIR)/sprs.elf \
> > +	$(TEST_DIR)/interrupts.elf
> >   
> >   tests-all = $(tests-common) $(tests)
> >   all: directories $(TEST_DIR)/boot_rom.bin $(tests-all)
> > diff --git a/powerpc/interrupts.c b/powerpc/interrupts.c
> > new file mode 100644
> > index 000000000..442f8c569
> > --- /dev/null
> > +++ b/powerpc/interrupts.c
> > @@ -0,0 +1,415 @@
> > +/*
> > + * Test interrupts
> > + *
> > + * Copyright 2024 Nicholas Piggin, IBM Corp.
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.
>
> I know, we're using this line in a lot of source files ... but maybe we 
> should do better for new files at least: "LGPL, version 2" is a little bit 
> ambiguous: Does it mean the "Library GPL version 2.0" or the "Lesser GPL 
> version 2.1"? Maybe you could clarify by additionally providing a SPDX 
> identifier here, or by explicitly writing 2.0 or 2.1.

Sure I'll try to improve this for all the new files.

Thanks,
Nick
Nicholas Piggin March 5, 2024, 2:30 a.m. UTC | #6
On Fri Mar 1, 2024 at 11:45 PM AEST, Andrew Jones wrote:
> On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
> > On 26/02/2024 11.12, Nicholas Piggin wrote:
> > > Add basic testing of various kinds of interrupts, machine check,
> > > page fault, illegal, decrementer, trace, syscall, etc.
> > > 
> > > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > > can be incorrectly set to 0.
> > 
> > Two questions out of curiosity:
> > 
> > Any chance that this could be fixed easily in QEMU?
> > 
> > Or is there a way to detect TCG from within the test? (for example, we have
> > a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
> > tests that are known to fail on TCG there)
>
> If there's nothing better, then it should be possible to check the
> QEMU_ACCEL environment variable which will be there with the default
> environ.
>
> > 
> > > @@ -0,0 +1,415 @@
> > > +/*
> > > + * Test interrupts
> > > + *
> > > + * Copyright 2024 Nicholas Piggin, IBM Corp.
> > > + *
> > > + * This work is licensed under the terms of the GNU LGPL, version 2.
> > 
> > I know, we're using this line in a lot of source files ... but maybe we
> > should do better for new files at least: "LGPL, version 2" is a little bit
> > ambiguous: Does it mean the "Library GPL version 2.0" or the "Lesser GPL
> > version 2.1"? Maybe you could clarify by additionally providing a SPDX
> > identifier here, or by explicitly writing 2.0 or 2.1.
>
> Let's only add SPDX identifiers to new files.

+1

Speaking of which, a bunch of these just got inherited from the file
that was copied to begin with (I tried not to remove copyright
notices unless there was really nothing of the original remaining).
So for new code/files, is there any particular preference for the
license to use? I don't much mind between the *GPL*. Looks like almost
all the SPDX code use GPL 2.0 only, but that could be just from
coming from Linux. I might just go with that.

Thanks,
Nick
Nicholas Piggin March 5, 2024, 2:35 a.m. UTC | #7
On Sat Mar 2, 2024 at 12:14 AM AEST, Andrew Jones wrote:
> On Fri, Mar 01, 2024 at 02:57:04PM +0100, Thomas Huth wrote:
> > On 01/03/2024 14.45, Andrew Jones wrote:
> > > On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
> > > > On 26/02/2024 11.12, Nicholas Piggin wrote:
> > > > > Add basic testing of various kinds of interrupts, machine check,
> > > > > page fault, illegal, decrementer, trace, syscall, etc.
> > > > > 
> > > > > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > > > > can be incorrectly set to 0.
> > > > 
> > > > Two questions out of curiosity:
> > > > 
> > > > Any chance that this could be fixed easily in QEMU?
> > > > 
> > > > Or is there a way to detect TCG from within the test? (for example, we have
> > > > a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
> > > > tests that are known to fail on TCG there)
> > > 
> > > If there's nothing better, then it should be possible to check the
> > > QEMU_ACCEL environment variable which will be there with the default
> > > environ.
> > 
> > Well, but that's only available from the host side, not within the test
> > (i.e. the guest). So that does not help much with report_xfail...
>
> powerpc has had environment variables in guests since commit f266c3e8ef15
> ("powerpc: enable environ"). QEMU_ACCEL is one of the environment
> variables given to unit tests by default when ENVIRON_DEFAULT is 'yes', as
> is the default set in configure. But...
>
> > I was rather thinking of something like checking the device tree, e.g. for
> > the compatible property in /hypervisor to see whether it's KVM or TCG...?
>
> ...while QEMU_ACCEL will work when the environ is present, DT will always
> be present, so checking the hypervisor node sounds better to me.

Yeah I got that.

One issue with xfail I noted when looking at this earlier, is that it
*always* expects a fail, and fails if it succeeds. So if you fix a QEMU
bug then you introduce a fail to kvm-unit-tests. So we really want a
report_known_bug(cond, "SPRs look sane", "QEMU TCG before v9.0 and
POWER9 DD2.0 is known to fail..."); and that could report a maybe-fail
that doesn't make the test group fail.

Thanks,
Nick
Thomas Huth March 5, 2024, 6:18 a.m. UTC | #8
On 05/03/2024 03.30, Nicholas Piggin wrote:
> On Fri Mar 1, 2024 at 11:45 PM AEST, Andrew Jones wrote:
>> On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
>>> On 26/02/2024 11.12, Nicholas Piggin wrote:
>>>> Add basic testing of various kinds of interrupts, machine check,
>>>> page fault, illegal, decrementer, trace, syscall, etc.
>>>>
>>>> This has a known failure on QEMU TCG pseries machines where MSR[ME]
>>>> can be incorrectly set to 0.
>>>
>>> Two questions out of curiosity:
>>>
>>> Any chance that this could be fixed easily in QEMU?
>>>
>>> Or is there a way to detect TCG from within the test? (for example, we have
>>> a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
>>> tests that are known to fail on TCG there)
>>
>> If there's nothing better, then it should be possible to check the
>> QEMU_ACCEL environment variable which will be there with the default
>> environ.
>>
>>>
>>>> @@ -0,0 +1,415 @@
>>>> +/*
>>>> + * Test interrupts
>>>> + *
>>>> + * Copyright 2024 Nicholas Piggin, IBM Corp.
>>>> + *
>>>> + * This work is licensed under the terms of the GNU LGPL, version 2.
>>>
>>> I know, we're using this line in a lot of source files ... but maybe we
>>> should do better for new files at least: "LGPL, version 2" is a little bit
>>> ambiguous: Does it mean the "Library GPL version 2.0" or the "Lesser GPL
>>> version 2.1"? Maybe you could clarify by additionally providing a SPDX
>>> identifier here, or by explicitly writing 2.0 or 2.1.
>>
>> Let's only add SPDX identifiers to new files.
> 
> +1
> 
> Speaking of which, a bunch of these just got inherited from the file
> that was copied to begin with (I tried not to remove copyright
> notices unless there was really nothing of the original remaining).
> So for new code/files, is there any particular preference for the
> license to use? I don't much mind between the *GPL*. Looks like almost
> all the SPDX code use GPL 2.0 only, but that could be just from
> coming from Linux. I might just go with that.

k-u-t were originally licensed under the LGPL, but in the course of time, 
code has been copied from the Linux kernel here and there, so we updated the 
license to GPL 2.
So yes, for code that might have been copied from the kernel, we have to use 
GPL 2. For other code, it's up to the author to chose. (IIRC we once 
discussed that LGPL would be nice for the files in the lib/ directory, while 
GPL is fine for the other folders, but my memory might fail here and it was 
never written in stone anyway)

  Thomas
Thomas Huth March 5, 2024, 6:26 a.m. UTC | #9
On 05/03/2024 03.19, Nicholas Piggin wrote:
> On Fri Mar 1, 2024 at 10:41 PM AEST, Thomas Huth wrote:
>> On 26/02/2024 11.12, Nicholas Piggin wrote:
>>> Add basic testing of various kinds of interrupts, machine check,
>>> page fault, illegal, decrementer, trace, syscall, etc.
>>>
>>> This has a known failure on QEMU TCG pseries machines where MSR[ME]
>>> can be incorrectly set to 0.
>>
>> Two questions out of curiosity:
>>
>> Any chance that this could be fixed easily in QEMU?
> 
> Yes I have a fix on the mailing list. It should get into 9.0 and
> probably stable.

Ok, then it's IMHO not worth the effort to make the k-u-t work around this 
bug in older QEMU versions.

>> Or is there a way to detect TCG from within the test? (for example, we have
>> a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
>> tests that are known to fail on TCG there)
> 
> I do have a half-done patch which adds exactly this.
> 
> One (minor) annoyance is that it doesn't seem possible to detect QEMU
> version to add workarounds. E.g., we would like to test the fixed
> functionality, but older qemu should not. Maybe that's going too much
> into a rabbit hole. We *could* put a QEMU version into device tree
> to deal with this though...

No, let's better not do this - hardwired version checks are often a bad 
idea, e.g. when a bug is in QEMU 8.0.0 and 8.1.0, it could be fixed in 8.0.1 
and then it could get really messy with the version checks...

  Thomas
Andrew Jones March 5, 2024, 12:12 p.m. UTC | #10
On Tue, Mar 05, 2024 at 07:26:18AM +0100, Thomas Huth wrote:
> On 05/03/2024 03.19, Nicholas Piggin wrote:
> > On Fri Mar 1, 2024 at 10:41 PM AEST, Thomas Huth wrote:
> > > On 26/02/2024 11.12, Nicholas Piggin wrote:
> > > > Add basic testing of various kinds of interrupts, machine check,
> > > > page fault, illegal, decrementer, trace, syscall, etc.
> > > > 
> > > > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > > > can be incorrectly set to 0.
> > > 
> > > Two questions out of curiosity:
> > > 
> > > Any chance that this could be fixed easily in QEMU?
> > 
> > Yes I have a fix on the mailing list. It should get into 9.0 and
> > probably stable.
> 
> Ok, then it's IMHO not worth the effort to make the k-u-t work around this
> bug in older QEMU versions.
> 
> > > Or is there a way to detect TCG from within the test? (for example, we have
> > > a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
> > > tests that are known to fail on TCG there)
> > 
> > I do have a half-done patch which adds exactly this.
> > 
> > One (minor) annoyance is that it doesn't seem possible to detect QEMU
> > version to add workarounds. E.g., we would like to test the fixed
> > functionality, but older qemu should not. Maybe that's going too much
> > into a rabbit hole. We *could* put a QEMU version into device tree
> > to deal with this though...
> 
> No, let's better not do this - hardwired version checks are often a bad
> idea, e.g. when a bug is in QEMU 8.0.0 and 8.1.0, it could be fixed in 8.0.1
> and then it could get really messy with the version checks...
>

We've tried to address this type of issue (but for KVM, so kernel versions
instead of QEMU versions) in the past by inventing the errata framework,
which is based on environment variables. Instead of checking for versions,
we check for a hash (which is just the commit hash of the fix). While we
do guess that the fix is present by version number, it can always be
manually set as present as well. In any case, the test is simply skipped
when the errata environment variable isn't present, so in the worst case
we lose some coverage we could have had, but the rest of the tests still
complete and we don't get the same failures over and over. An example of
its use is in arm/psci.c. Look for the ERRATA() calls.

We could extend the errata framework for QEMU/TCG. We just need to add
another bit of data to the errata.txt file for it to know it should
check QEMU versions instead of kernel versions for those errata. We can
also ignore the errata framework and just create the errata environment
variable which would by 'n' by default now and later, after distros have
fixes, it could be changed to 'y'.

Thanks,
drew
diff mbox series

Patch

diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
index cf1b9d8ff..eed37d1f4 100644
--- a/lib/powerpc/asm/processor.h
+++ b/lib/powerpc/asm/processor.h
@@ -11,7 +11,11 @@  void do_handle_exception(struct pt_regs *regs);
 #endif /* __ASSEMBLY__ */
 
 extern bool cpu_has_hv;
+extern bool cpu_has_power_mce;
+extern bool cpu_has_siar;
 extern bool cpu_has_heai;
+extern bool cpu_has_prefix;
+extern bool cpu_has_sc_lev;
 
 static inline uint64_t mfspr(int nr)
 {
diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
index 782e75527..d6097f48f 100644
--- a/lib/powerpc/asm/reg.h
+++ b/lib/powerpc/asm/reg.h
@@ -5,8 +5,15 @@ 
 
 #define UL(x) _AC(x, UL)
 
+#define SPR_DSISR	0x012
+#define SPR_DAR		0x013
+#define SPR_DEC		0x016
 #define SPR_SRR0	0x01a
 #define SPR_SRR1	0x01b
+#define   SRR1_PREFIX		UL(0x20000000)
+#define SPR_FSCR	0x099
+#define   FSCR_PREFIX		UL(0x2000)
+#define SPR_HFSCR	0x0be
 #define SPR_TB		0x10c
 #define SPR_SPRG0	0x110
 #define SPR_SPRG1	0x111
@@ -22,12 +29,17 @@ 
 #define   PVR_VER_POWER8	UL(0x004d0000)
 #define   PVR_VER_POWER9	UL(0x004e0000)
 #define   PVR_VER_POWER10	UL(0x00800000)
+#define SPR_HDEC	0x136
 #define SPR_HSRR0	0x13a
 #define SPR_HSRR1	0x13b
+#define SPR_LPCR	0x13e
+#define   LPCR_HDICE		UL(0x1)
+#define SPR_HEIR	0x153
 #define SPR_MMCR0	0x31b
 #define   MMCR0_FC		UL(0x80000000)
 #define   MMCR0_PMAE		UL(0x04000000)
 #define   MMCR0_PMAO		UL(0x00000080)
+#define SPR_SIAR	0x31c
 
 /* Machine State Register definitions: */
 #define MSR_LE_BIT	0
@@ -35,6 +47,11 @@ 
 #define MSR_HV_BIT	60			/* Hypervisor mode */
 #define MSR_SF_BIT	63			/* 64-bit mode */
 
+#define MSR_DR		UL(0x0010)
+#define MSR_IR		UL(0x0020)
+#define MSR_BE		UL(0x0200)		/* Branch Trace Enable */
+#define MSR_SE		UL(0x0400)		/* Single Step Enable */
+#define MSR_EE		UL(0x8000)
 #define MSR_ME		UL(0x1000)
 
 #endif
diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
index 3c81aee9e..9b665f59c 100644
--- a/lib/powerpc/setup.c
+++ b/lib/powerpc/setup.c
@@ -87,7 +87,11 @@  static void cpu_set(int fdtnode, u64 regval, void *info)
 }
 
 bool cpu_has_hv;
+bool cpu_has_power_mce; /* POWER CPU machine checks */
+bool cpu_has_siar;
 bool cpu_has_heai;
+bool cpu_has_prefix;
+bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
 
 static void cpu_init(void)
 {
@@ -112,15 +116,22 @@  static void cpu_init(void)
 
 	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
 	case PVR_VER_POWER10:
+		cpu_has_prefix = true;
+		cpu_has_sc_lev = true;
 	case PVR_VER_POWER9:
 	case PVR_VER_POWER8E:
 	case PVR_VER_POWER8NVL:
 	case PVR_VER_POWER8:
+		cpu_has_power_mce = true;
 		cpu_has_heai = true;
+		cpu_has_siar = true;
 		break;
 	default:
 		break;
 	}
+
+	if (!cpu_has_hv) /* HEIR is HV register */
+		cpu_has_heai = false;
 }
 
 static void mem_init(phys_addr_t freemem_start)
diff --git a/lib/ppc64/asm/ptrace.h b/lib/ppc64/asm/ptrace.h
index 12de7499b..db263a59e 100644
--- a/lib/ppc64/asm/ptrace.h
+++ b/lib/ppc64/asm/ptrace.h
@@ -5,6 +5,9 @@ 
 #define STACK_FRAME_OVERHEAD    112     /* size of minimum stack frame */
 
 #ifndef __ASSEMBLY__
+
+#include <asm/reg.h>
+
 struct pt_regs {
 	unsigned long gpr[32];
 	unsigned long nip;
@@ -17,6 +20,19 @@  struct pt_regs {
 	unsigned long _pad; /* stack must be 16-byte aligned */
 };
 
+static inline bool regs_is_prefix(volatile struct pt_regs *regs)
+{
+	return regs->msr & SRR1_PREFIX;
+}
+
+static inline void regs_advance_insn(struct pt_regs *regs)
+{
+	if (regs_is_prefix(regs))
+		regs->nip += 8;
+	else
+		regs->nip += 4;
+}
+
 #define STACK_INT_FRAME_SIZE    (sizeof(struct pt_regs) + \
 				 STACK_FRAME_OVERHEAD + KERNEL_REDZONE_SIZE)
 
diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index 1e181da69..68165fc25 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -12,7 +12,8 @@  tests-common = \
 	$(TEST_DIR)/rtas.elf \
 	$(TEST_DIR)/emulator.elf \
 	$(TEST_DIR)/tm.elf \
-	$(TEST_DIR)/sprs.elf
+	$(TEST_DIR)/sprs.elf \
+	$(TEST_DIR)/interrupts.elf
 
 tests-all = $(tests-common) $(tests)
 all: directories $(TEST_DIR)/boot_rom.bin $(tests-all)
diff --git a/powerpc/interrupts.c b/powerpc/interrupts.c
new file mode 100644
index 000000000..442f8c569
--- /dev/null
+++ b/powerpc/interrupts.c
@@ -0,0 +1,415 @@ 
+/*
+ * Test interrupts
+ *
+ * Copyright 2024 Nicholas Piggin, IBM Corp.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include <libcflat.h>
+#include <util.h>
+#include <migrate.h>
+#include <alloc.h>
+#include <asm/setup.h>
+#include <asm/handlers.h>
+#include <asm/hcall.h>
+#include <asm/processor.h>
+#include <asm/time.h>
+#include <asm/barrier.h>
+
+static volatile bool got_interrupt;
+static volatile struct pt_regs recorded_regs;
+
+static void mce_handler(struct pt_regs *regs, void *opaque)
+{
+	bool *is_fetch = opaque;
+
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	if (*is_fetch)
+		regs->nip = regs->link;
+	else
+		regs_advance_insn(regs);
+}
+
+static void fault_handler(struct pt_regs *regs, void *opaque)
+{
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	if (regs->trap == 0x400 || regs->trap == 0x480)
+		regs->nip = regs->link;
+	else
+		regs_advance_insn(regs);
+}
+
+static void test_mce(void)
+{
+	unsigned long addr = -4ULL;
+	uint8_t tmp;
+	bool is_fetch;
+
+	report_prefix_push("mce");
+
+	handle_exception(0x200, mce_handler, &is_fetch);
+	handle_exception(0x300, fault_handler, NULL);
+	handle_exception(0x380, fault_handler, NULL);
+	handle_exception(0x400, fault_handler, NULL);
+	handle_exception(0x480, fault_handler, NULL);
+
+	if (machine_is_powernv()) {
+		enable_mcheck();
+	} else {
+		report(mfmsr() & MSR_ME, "pseries machine has MSR[ME]=1");
+		if (!(mfmsr() & MSR_ME)) { /* try to fix it */
+			enable_mcheck();
+		}
+		if (mfmsr() & MSR_ME) {
+			disable_mcheck();
+			report(mfmsr() & MSR_ME, "pseries is unable to change MSR[ME]");
+			if (!(mfmsr() & MSR_ME)) { /* try to fix it */
+				enable_mcheck();
+			}
+		}
+	}
+
+	is_fetch = false;
+	asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr));
+
+	report(got_interrupt, "MCE on access to invalid real address");
+	if (got_interrupt) {
+		report(mfspr(SPR_DAR) == addr, "MCE sets DAR correctly");
+		if (cpu_has_power_mce)
+			report(recorded_regs.msr & (1ULL << 21), "d-side MCE sets SRR1[42]");
+		got_interrupt = false;
+	}
+
+	is_fetch = true;
+	asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr");
+	report(got_interrupt, "MCE on fetch from invalid real address");
+	if (got_interrupt) {
+		report(recorded_regs.nip == addr, "MCE sets SRR0 correctly");
+		if (cpu_has_power_mce)
+			report(!(recorded_regs.msr & (1ULL << 21)), "i-side MCE clears SRR1[42]");
+		got_interrupt = false;
+	}
+
+	handle_exception(0x200, NULL, NULL);
+	handle_exception(0x300, NULL, NULL);
+	handle_exception(0x380, NULL, NULL);
+	handle_exception(0x400, NULL, NULL);
+	handle_exception(0x480, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+static void dseg_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs_advance_insn(regs);
+	regs->msr &= ~MSR_DR;
+}
+
+static void test_dseg(void)
+{
+	uint64_t msr, tmp;
+
+	report_prefix_push("data segment");
+
+	/* Some HV start in radix mode and need 0x300 */
+	handle_exception(0x300, &dseg_handler, NULL);
+	handle_exception(0x380, &dseg_handler, NULL);
+
+	asm volatile(
+"		mfmsr	%0		\n \
+		ori	%0,%0,%2	\n \
+		mtmsrd	%0		\n \
+		lbz	%1,0(0)		"
+		: "=r"(msr), "=r"(tmp) : "i"(MSR_DR): "memory");
+
+	report(got_interrupt, "interrupt on NULL dereference");
+	got_interrupt = false;
+
+	handle_exception(0x300, NULL, NULL);
+	handle_exception(0x380, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+static void dec_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs->msr &= ~MSR_EE;
+}
+
+static void test_dec(void)
+{
+	uint64_t msr;
+	uint64_t tb;
+
+	report_prefix_push("decrementer");
+
+	handle_exception(0x900, &dec_handler, NULL);
+
+	asm volatile(
+"		mtdec	%1		\n \
+		mfmsr	%0		\n \
+		ori	%0,%0,%2	\n \
+		mtmsrd	%0,1		"
+		: "=r"(msr) : "r"(10000), "i"(MSR_EE): "memory");
+
+	tb = get_tb();
+	while (!got_interrupt) {
+		if (get_tb() - tb > tb_hz * 5)
+			break; /* timeout 5s */
+	}
+
+	report(got_interrupt, "interrupt on decrementer underflow");
+	got_interrupt = false;
+
+	handle_exception(0x900, NULL, NULL);
+
+	if (!machine_is_powernv())
+		goto done; /* Skip HV tests */
+
+	handle_exception(0x980, &dec_handler, NULL);
+
+	mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_HDICE);
+	asm volatile(
+"		mtspr	0x136,%1	\n \
+		mtdec	%3		\n \
+		mfmsr	%0		\n \
+		ori	%0,%0,%2	\n \
+		mtmsrd	%0,1		"
+		: "=r"(msr) : "r"(10000), "i"(MSR_EE), "r"(0x7fffffff): "memory");
+
+	tb = get_tb();
+	while (!got_interrupt) {
+		if (get_tb() - tb > tb_hz * 5)
+			break; /* timeout 5s */
+	}
+
+	mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_HDICE);
+
+	report(got_interrupt, "interrupt on hdecrementer underflow");
+	got_interrupt = false;
+
+	handle_exception(0x980, NULL, NULL);
+
+done:
+	report_prefix_pop();
+}
+
+
+static volatile uint64_t recorded_heir;
+
+static void heai_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs_advance_insn(regs);
+	if (cpu_has_heai)
+		recorded_heir = mfspr(SPR_HEIR);
+}
+
+static void program_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs_advance_insn(regs);
+}
+
+/*
+ * This tests invalid instruction handling. powernv (HV) should take an
+ * HEAI interrupt with the HEIR SPR set to the instruction image. pseries
+ * (guest) should take a program interrupt. CPUs which support prefix
+ * should report prefix instruction in (H)SRR1[34].
+ */
+static void test_illegal(void)
+{
+	report_prefix_push("illegal instruction");
+
+	if (machine_is_powernv()) {
+		handle_exception(0xe40, &heai_handler, NULL);
+	} else {
+		handle_exception(0x700, &program_handler, NULL);
+	}
+
+	asm volatile(".long 0x12345678" ::: "memory");
+	report(got_interrupt, "interrupt on invalid instruction");
+	got_interrupt = false;
+	if (cpu_has_heai)
+		report(recorded_heir == 0x12345678, "HEIR: 0x%08lx", recorded_heir);
+	report(!regs_is_prefix(&recorded_regs), "(H)SRR1 prefix bit clear");
+
+	if (cpu_has_prefix) {
+		asm volatile(".balign 8 ; .long 0x04000123; .long 0x00badc0d");
+		report(got_interrupt, "interrupt on invalid prefix instruction");
+		got_interrupt = false;
+		if (cpu_has_heai)
+			report(recorded_heir == 0x0400012300badc0d, "HEIR: 0x%08lx", recorded_heir);
+		report(regs_is_prefix(&recorded_regs), "(H)SRR1 prefix bit set");
+	}
+
+	handle_exception(0xe40, NULL, NULL);
+	handle_exception(0x700, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+static void sc_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+}
+
+static void test_sc(void)
+{
+	report_prefix_push("syscall");
+
+	handle_exception(0xc00, &sc_handler, NULL);
+
+	asm volatile("sc 0" ::: "memory");
+
+	report(got_interrupt, "interrupt on sc 0 instruction");
+	got_interrupt = false;
+	if (cpu_has_sc_lev)
+		report(((recorded_regs.msr >> 20) & 0x3) == 0, "SRR1 set LEV=0");
+	if (machine_is_powernv()) {
+		asm volatile("sc 1" ::: "memory");
+
+		report(got_interrupt, "interrupt on sc 1 instruction");
+		got_interrupt = false;
+		if (cpu_has_sc_lev)
+			report(((recorded_regs.msr >> 20) & 0x3) == 1, "SRR1 set LEV=1");
+	}
+
+	handle_exception(0xc00, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+
+static void trace_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs->msr &= ~(MSR_SE | MSR_BE);
+}
+
+static void program_trace_handler(struct pt_regs *regs, void *data)
+{
+	regs->msr &= ~(MSR_SE | MSR_BE);
+	regs->nip += 4;
+}
+
+extern char trace_insn[];
+extern char trace_insn2[];
+extern char trace_insn3[];
+extern char trace_rfid[];
+
+static void test_trace(void)
+{
+	unsigned long msr;
+
+	report_prefix_push("trace");
+
+	handle_exception(0xd00, &trace_handler, NULL);
+
+	msr = mfmsr() | MSR_SE;
+	asm volatile(
+	"	mtmsr	%0		\n"
+	".global trace_insn		\n"
+	"trace_insn:			\n"
+	"	nop			\n"
+	: : "r"(msr) : "memory");
+
+	report(got_interrupt, "interrupt on single step");
+	got_interrupt = false;
+	report(recorded_regs.nip == (unsigned long)trace_insn + 4,
+			"single step interrupt at the correct address");
+	if (cpu_has_siar)
+		report(mfspr(SPR_SIAR) == (unsigned long)trace_insn,
+			"single step recorded SIAR at the correct address");
+
+	msr = mfmsr() | MSR_SE;
+	asm volatile(
+	"	mtmsr	%0		\n"
+	".global trace_insn2		\n"
+	"trace_insn2:			\n"
+	"	b	1f		\n"
+	"	nop			\n"
+	"1:				\n"
+	: : "r"(msr) : "memory");
+
+	report(got_interrupt, "interrupt on single step branch");
+	got_interrupt = false;
+	report(recorded_regs.nip == (unsigned long)trace_insn2 + 8,
+			"single step interrupt at the correct address");
+	if (cpu_has_siar)
+		report(mfspr(SPR_SIAR) == (unsigned long)trace_insn2,
+			"single step recorded SIAR at the correct address");
+
+	msr = mfmsr() | MSR_BE;
+	asm volatile(
+	"	mtmsr	%0		\n"
+	".global trace_insn3		\n"
+	"trace_insn3:			\n"
+	"	nop			\n"
+	"	b	1f		\n"
+	"	nop			\n"
+	"1:				\n"
+	: : "r"(msr) : "memory");
+
+	report(got_interrupt, "interrupt on branch trace");
+	got_interrupt = false;
+	report(recorded_regs.nip == (unsigned long)trace_insn3 + 12,
+			"branch trace interrupt at the correct address");
+	if (cpu_has_siar)
+		report(mfspr(SPR_SIAR) == (unsigned long)trace_insn3 + 4,
+			"branch trace recorded SIAR at the correct address");
+
+	handle_exception(0x700, &program_trace_handler, NULL);
+	msr = mfmsr() | MSR_SE;
+	asm volatile(
+	"	mtmsr	%0		\n"
+	"	trap			\n"
+	: : "r"(msr) : "memory");
+
+	report(!got_interrupt, "no interrupt on single step trap");
+	got_interrupt = false;
+	handle_exception(0x700, NULL, NULL);
+
+	msr = mfmsr() | MSR_SE;
+	mtspr(SPR_SRR0, (unsigned long)trace_rfid);
+	mtspr(SPR_SRR1, mfmsr());
+	asm volatile(
+	"	mtmsr	%0		\n"
+	"	rfid			\n"
+	".global trace_rfid		\n"
+	"trace_rfid:			\n"
+	: : "r"(msr) : "memory");
+
+	report(!got_interrupt, "no interrupt on single step rfid");
+	got_interrupt = false;
+	handle_exception(0xd00, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+
+int main(int argc, char **argv)
+{
+	report_prefix_push("interrupts");
+
+	if (cpu_has_power_mce)
+		test_mce();
+	test_dseg();
+	test_illegal();
+	test_dec();
+	test_sc();
+	test_trace();
+
+	report_prefix_pop();
+
+	return report_summary();
+}
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index 41bb8a327..93c54f52a 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -86,6 +86,9 @@  groups = rtas
 [emulator]
 file = emulator.elf
 
+[interrupts]
+file = interrupts.elf
+
 [h_cede_tm]
 file = tm.elf
 machine = pseries