diff mbox series

[v3,2/2] target/ppc: Add support for scv and rfscv instructions

Message ID 20200317054918.199161-2-npiggin@gmail.com (mailing list archive)
State New, archived
Headers show
Series [v3,1/2] target/ppc: Improve syscall exception logging | expand

Commit Message

Nicholas Piggin March 17, 2020, 5:49 a.m. UTC
POWER9 adds scv and rfscv instructions and the system call vectored
interrupt. Linux does not support this instruction yet but it has
been tested with a modified kernel that runs on real hardware.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
Since v2:
- Rebased on top of FWNMI series

 linux-user/ppc/cpu_loop.c       |  1 +
 target/ppc/cpu.h                |  7 ++-
 target/ppc/excp_helper.c        | 98 ++++++++++++++++++++++++---------
 target/ppc/helper.h             |  1 +
 target/ppc/translate.c          | 46 +++++++++++++++-
 target/ppc/translate_init.inc.c |  3 +-
 6 files changed, 126 insertions(+), 30 deletions(-)

Comments

David Gibson March 19, 2020, 6:22 a.m. UTC | #1
On Tue, Mar 17, 2020 at 03:49:18PM +1000, Nicholas Piggin wrote:
> POWER9 adds scv and rfscv instructions and the system call vectored
> interrupt. Linux does not support this instruction yet but it has
> been tested with a modified kernel that runs on real hardware.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>

Applied to ppc-for-5.1.

> ---
> Since v2:
> - Rebased on top of FWNMI series
> 
>  linux-user/ppc/cpu_loop.c       |  1 +
>  target/ppc/cpu.h                |  7 ++-
>  target/ppc/excp_helper.c        | 98 ++++++++++++++++++++++++---------
>  target/ppc/helper.h             |  1 +
>  target/ppc/translate.c          | 46 +++++++++++++++-
>  target/ppc/translate_init.inc.c |  3 +-
>  6 files changed, 126 insertions(+), 30 deletions(-)
> 
> diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c
> index 5b27f8603e..df71e15a25 100644
> --- a/linux-user/ppc/cpu_loop.c
> +++ b/linux-user/ppc/cpu_loop.c
> @@ -267,6 +267,7 @@ void cpu_loop(CPUPPCState *env)
>              queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
>              break;
>          case POWERPC_EXCP_SYSCALL:  /* System call exception                 */
> +        case POWERPC_EXCP_SYSCALL_VECTORED:
>              cpu_abort(cs, "Syscall exception while in user mode. "
>                        "Aborting\n");
>              break;
> diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
> index ed8d2015bd..992f0a49e8 100644
> --- a/target/ppc/cpu.h
> +++ b/target/ppc/cpu.h
> @@ -127,8 +127,9 @@ enum {
>      POWERPC_EXCP_SDOOR_HV = 100,
>      /* ISA 3.00 additions */
>      POWERPC_EXCP_HVIRT    = 101,
> +    POWERPC_EXCP_SYSCALL_VECTORED = 102, /* scv exception                     */
>      /* EOL                                                                   */
> -    POWERPC_EXCP_NB       = 102,
> +    POWERPC_EXCP_NB       = 103,
>      /* QEMU exceptions: used internally during code translation              */
>      POWERPC_EXCP_STOP         = 0x200, /* stop translation                   */
>      POWERPC_EXCP_BRANCH       = 0x201, /* branch instruction                 */
> @@ -475,6 +476,7 @@ typedef struct ppc_v3_pate_t {
>  /* Facility Status and Control (FSCR) bits */
>  #define FSCR_EBB        (63 - 56) /* Event-Based Branch Facility */
>  #define FSCR_TAR        (63 - 55) /* Target Address Register */
> +#define FSCR_SCV        (63 - 51) /* System call vectored */
>  /* Interrupt cause mask and position in FSCR. HFSCR has the same format */
>  #define FSCR_IC_MASK    (0xFFULL)
>  #define FSCR_IC_POS     (63 - 7)
> @@ -484,6 +486,7 @@ typedef struct ppc_v3_pate_t {
>  #define FSCR_IC_TM          5
>  #define FSCR_IC_EBB         7
>  #define FSCR_IC_TAR         8
> +#define FSCR_IC_SCV        12
>  
>  /* Exception state register bits definition                                  */
>  #define ESR_PIL   PPC_BIT(36) /* Illegal Instruction                    */
> @@ -551,6 +554,8 @@ enum {
>      POWERPC_FLAG_VSX      = 0x00080000,
>      /* Has Transaction Memory (ISA 2.07)                                     */
>      POWERPC_FLAG_TM       = 0x00100000,
> +    /* Has SCV (ISA 3.00)                                                    */
> +    POWERPC_FLAG_SCV      = 0x00200000,
>  };
>  
>  /*****************************************************************************/
> diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
> index 81ee19ebae..73b5c28d03 100644
> --- a/target/ppc/excp_helper.c
> +++ b/target/ppc/excp_helper.c
> @@ -67,6 +67,18 @@ static inline void dump_syscall(CPUPPCState *env)
>                    ppc_dump_gpr(env, 8), env->nip);
>  }
>  
> +static inline void dump_syscall_vectored(CPUPPCState *env)
> +{
> +    qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64
> +                  " r3=%016" PRIx64 " r4=%016" PRIx64 " r5=%016" PRIx64
> +                  " r6=%016" PRIx64 " r7=%016" PRIx64 " r8=%016" PRIx64
> +                  " nip=" TARGET_FMT_lx "\n",
> +                  ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3),
> +                  ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5),
> +                  ppc_dump_gpr(env, 6), ppc_dump_gpr(env, 7),
> +                  ppc_dump_gpr(env, 8), env->nip);
> +}
> +
>  static inline void dump_hcall(CPUPPCState *env)
>  {
>      qemu_log_mask(CPU_LOG_INT, "hypercall r3=%016" PRIx64
> @@ -185,7 +197,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
>      CPUState *cs = CPU(cpu);
>      CPUPPCState *env = &cpu->env;
>      target_ulong msr, new_msr, vector;
> -    int srr0, srr1, asrr0, asrr1, lev, ail;
> +    int srr0, srr1, asrr0, asrr1, lev = -1, ail;
>      bool lpes0;
>  
>      qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
> @@ -421,6 +433,13 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
>              new_msr |= (target_ulong)MSR_HVB;
>          }
>          break;
> +    case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception                     */
> +        lev = env->error_code;
> +        dump_syscall_vectored(env);
> +        env->nip += 4;
> +        new_msr |= env->msr & ((target_ulong)1 << MSR_EE);
> +        new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
> +        break;
>      case POWERPC_EXCP_FPU:       /* Floating-point unavailable exception     */
>      case POWERPC_EXCP_APU:       /* Auxiliary processor unavailable          */
>      case POWERPC_EXCP_DECR:      /* Decrementer exception                    */
> @@ -723,12 +742,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
>          break;
>      }
>  
> -    /* Save PC */
> -    env->spr[srr0] = env->nip;
> -
> -    /* Save MSR */
> -    env->spr[srr1] = msr;
> -
>      /* Sanity check */
>      if (!(env->msr_mask & MSR_HVB)) {
>          if (new_msr & MSR_HVB) {
> @@ -741,14 +754,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
>          }
>      }
>  
> -    /* If any alternate SRR register are defined, duplicate saved values */
> -    if (asrr0 != -1) {
> -        env->spr[asrr0] = env->spr[srr0];
> -    }
> -    if (asrr1 != -1) {
> -        env->spr[asrr1] = env->spr[srr1];
> -    }
> -
>      /*
>       * Sort out endianness of interrupt, this differs depending on the
>       * CPU, the HV mode, etc...
> @@ -783,14 +788,6 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
>      }
>  #endif
>  
> -    /* Jump to handler */
> -    vector = env->excp_vectors[excp];
> -    if (vector == (target_ulong)-1ULL) {
> -        cpu_abort(cs, "Raised an exception without defined vector %d\n",
> -                  excp);
> -    }
> -    vector |= env->excp_prefix;
> -
>      /*
>       * AIL only works if there is no HV transition and we are running
>       * with translations enabled
> @@ -799,10 +796,21 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
>          ((new_msr & MSR_HVB) && !(msr & MSR_HVB))) {
>          ail = 0;
>      }
> -    /* Handle AIL */
> -    if (ail) {
> -        new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
> -        vector |= ppc_excp_vector_offset(cs, ail);
> +
> +    vector = env->excp_vectors[excp];
> +    if (vector == (target_ulong)-1ULL) {
> +        cpu_abort(cs, "Raised an exception without defined vector %d\n",
> +                  excp);
> +    }
> +
> +    vector |= env->excp_prefix;
> +
> +    /* If any alternate SRR register are defined, duplicate saved values */
> +    if (asrr0 != -1) {
> +        env->spr[asrr0] = env->nip;
> +    }
> +    if (asrr1 != -1) {
> +        env->spr[asrr1] = msr;
>      }
>  
>  #if defined(TARGET_PPC64)
> @@ -822,6 +830,37 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
>      }
>  #endif
>  
> +    if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
> +        /* Save PC */
> +        env->spr[srr0] = env->nip;
> +
> +        /* Save MSR */
> +        env->spr[srr1] = msr;
> +
> +        /* Handle AIL */
> +        if (ail) {
> +            new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
> +            vector |= ppc_excp_vector_offset(cs, ail);
> +        }
> +
> +#if defined(TARGET_PPC64)
> +    } else {
> +        /* scv AIL is a little different */
> +        if (ail) {
> +            new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
> +        }
> +        if (ail == AIL_C000_0000_0000_4000) {
> +            vector |= 0xc000000000003000ull;
> +        } else {
> +            vector |= 0x0000000000017000ull;
> +        }
> +        vector += lev * 0x20;
> +
> +        env->lr = env->nip;
> +        env->ctr = msr;
> +#endif
> +    }
> +
>      powerpc_set_excp_state(cpu, vector, new_msr);
>  }
>  
> @@ -1162,6 +1201,11 @@ void helper_rfid(CPUPPCState *env)
>      do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]);
>  }
>  
> +void helper_rfscv(CPUPPCState *env)
> +{
> +    do_rfi(env, env->lr, env->ctr);
> +}
> +
>  void helper_hrfid(CPUPPCState *env)
>  {
>      do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
> diff --git a/target/ppc/helper.h b/target/ppc/helper.h
> index cfb4c07085..ee1498050d 100644
> --- a/target/ppc/helper.h
> +++ b/target/ppc/helper.h
> @@ -15,6 +15,7 @@ DEF_HELPER_1(rfmci, void, env)
>  #if defined(TARGET_PPC64)
>  DEF_HELPER_2(pminsn, void, env, i32)
>  DEF_HELPER_1(rfid, void, env)
> +DEF_HELPER_1(rfscv, void, env)
>  DEF_HELPER_1(hrfid, void, env)
>  DEF_HELPER_2(store_lpcr, void, env, tl)
>  DEF_HELPER_2(store_pcr, void, env, tl)
> diff --git a/target/ppc/translate.c b/target/ppc/translate.c
> index 127c82a24e..eb0ddba850 100644
> --- a/target/ppc/translate.c
> +++ b/target/ppc/translate.c
> @@ -173,6 +173,7 @@ struct DisasContext {
>      bool vsx_enabled;
>      bool spe_enabled;
>      bool tm_enabled;
> +    bool scv_enabled;
>      bool gtse;
>      ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
>      int singlestep_enabled;
> @@ -4012,6 +4013,22 @@ static void gen_rfid(DisasContext *ctx)
>  #endif
>  }
>  
> +static void gen_rfscv(DisasContext *ctx)
> +{
> +#if defined(CONFIG_USER_ONLY)
> +    GEN_PRIV;
> +#else
> +    /* Restore CPU state */
> +    CHK_SV;
> +    if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
> +        gen_io_start();
> +    }
> +    gen_update_cfar(ctx, ctx->base.pc_next - 4);
> +    gen_helper_rfscv(cpu_env);
> +    gen_sync_exception(ctx);
> +#endif
> +}
> +
>  static void gen_hrfid(DisasContext *ctx)
>  {
>  #if defined(CONFIG_USER_ONLY)
> @@ -4030,6 +4047,7 @@ static void gen_hrfid(DisasContext *ctx)
>  #define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL_USER
>  #else
>  #define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL
> +#define POWERPC_SYSCALL_VECTORED POWERPC_EXCP_SYSCALL_VECTORED
>  #endif
>  static void gen_sc(DisasContext *ctx)
>  {
> @@ -4039,6 +4057,21 @@ static void gen_sc(DisasContext *ctx)
>      gen_exception_err(ctx, POWERPC_SYSCALL, lev);
>  }
>  
> +#if defined(TARGET_PPC64)
> +static void gen_scv(DisasContext *ctx)
> +{
> +    uint32_t lev;
> +
> +    if (unlikely(!ctx->scv_enabled)) {
> +        gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_SCV);
> +        return;
> +    }
> +
> +    lev = (ctx->opcode >> 5) & 0x7F;
> +    gen_exception_err(ctx, POWERPC_SYSCALL_VECTORED, lev);
> +}
> +#endif
> +
>  /***                                Trap                                   ***/
>  
>  /* Check for unconditional traps (always or never) */
> @@ -7019,6 +7052,10 @@ GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER),
>  GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW),
>  #if defined(TARGET_PPC64)
>  GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B),
> +/* Top bit of opc2 corresponds with low bit of LEV, so use two handlers */
> +GEN_HANDLER_E(scv, 0x11, 0x10, 0xFF, 0x03FFF01E, PPC_NONE, PPC2_ISA300),
> +GEN_HANDLER_E(scv, 0x11, 0x00, 0xFF, 0x03FFF01E, PPC_NONE, PPC2_ISA300),
> +GEN_HANDLER_E(rfscv, 0x13, 0x12, 0x02, 0x03FF8001, PPC_NONE, PPC2_ISA300),
>  GEN_HANDLER_E(stop, 0x13, 0x12, 0x0b, 0x03FFF801, PPC_NONE, PPC2_ISA300),
>  GEN_HANDLER_E(doze, 0x13, 0x12, 0x0c, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
>  GEN_HANDLER_E(nap, 0x13, 0x12, 0x0d, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
> @@ -7026,7 +7063,9 @@ GEN_HANDLER_E(sleep, 0x13, 0x12, 0x0e, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
>  GEN_HANDLER_E(rvwinkle, 0x13, 0x12, 0x0f, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
>  GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H),
>  #endif
> -GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFF01D, PPC_FLOW),
> +/* Top bit of opc2 corresponds with low bit of LEV, so use two handlers */
> +GEN_HANDLER(sc, 0x11, 0x11, 0xFF, 0x03FFF01D, PPC_FLOW),
> +GEN_HANDLER(sc, 0x11, 0x01, 0xFF, 0x03FFF01D, PPC_FLOW),
>  GEN_HANDLER(tw, 0x1F, 0x04, 0x00, 0x00000001, PPC_FLOW),
>  GEN_HANDLER(twi, 0x03, 0xFF, 0xFF, 0x00000000, PPC_FLOW),
>  #if defined(TARGET_PPC64)
> @@ -7805,6 +7844,11 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
>      } else {
>          ctx->vsx_enabled = false;
>      }
> +    if ((env->flags & POWERPC_FLAG_SCV) && (env->spr[SPR_FSCR] & (1ull << FSCR_SCV))) {
> +        ctx->scv_enabled = true;
> +    } else {
> +        ctx->scv_enabled = false;
> +    }
>  #if defined(TARGET_PPC64)
>      if ((env->flags & POWERPC_FLAG_TM) && msr_tm) {
>          ctx->tm_enabled = !!msr_tm;
> diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c
> index 0ae145e18d..15025cd5ea 100644
> --- a/target/ppc/translate_init.inc.c
> +++ b/target/ppc/translate_init.inc.c
> @@ -3382,6 +3382,7 @@ static void init_excp_POWER9(CPUPPCState *env)
>  
>  #if !defined(CONFIG_USER_ONLY)
>      env->excp_vectors[POWERPC_EXCP_HVIRT]    = 0x00000EA0;
> +    env->excp_vectors[POWERPC_EXCP_SYSCALL_VECTORED] = 0x00000000;
>  #endif
>  }
>  
> @@ -9030,7 +9031,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
>      pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
>                   POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
>                   POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR |
> -                 POWERPC_FLAG_VSX | POWERPC_FLAG_TM;
> +                 POWERPC_FLAG_VSX | POWERPC_FLAG_TM | POWERPC_FLAG_SCV;
>      pcc->l1_dcache_size = 0x8000;
>      pcc->l1_icache_size = 0x8000;
>      pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;
diff mbox series

Patch

diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c
index 5b27f8603e..df71e15a25 100644
--- a/linux-user/ppc/cpu_loop.c
+++ b/linux-user/ppc/cpu_loop.c
@@ -267,6 +267,7 @@  void cpu_loop(CPUPPCState *env)
             queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
             break;
         case POWERPC_EXCP_SYSCALL:  /* System call exception                 */
+        case POWERPC_EXCP_SYSCALL_VECTORED:
             cpu_abort(cs, "Syscall exception while in user mode. "
                       "Aborting\n");
             break;
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index ed8d2015bd..992f0a49e8 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -127,8 +127,9 @@  enum {
     POWERPC_EXCP_SDOOR_HV = 100,
     /* ISA 3.00 additions */
     POWERPC_EXCP_HVIRT    = 101,
+    POWERPC_EXCP_SYSCALL_VECTORED = 102, /* scv exception                     */
     /* EOL                                                                   */
-    POWERPC_EXCP_NB       = 102,
+    POWERPC_EXCP_NB       = 103,
     /* QEMU exceptions: used internally during code translation              */
     POWERPC_EXCP_STOP         = 0x200, /* stop translation                   */
     POWERPC_EXCP_BRANCH       = 0x201, /* branch instruction                 */
@@ -475,6 +476,7 @@  typedef struct ppc_v3_pate_t {
 /* Facility Status and Control (FSCR) bits */
 #define FSCR_EBB        (63 - 56) /* Event-Based Branch Facility */
 #define FSCR_TAR        (63 - 55) /* Target Address Register */
+#define FSCR_SCV        (63 - 51) /* System call vectored */
 /* Interrupt cause mask and position in FSCR. HFSCR has the same format */
 #define FSCR_IC_MASK    (0xFFULL)
 #define FSCR_IC_POS     (63 - 7)
@@ -484,6 +486,7 @@  typedef struct ppc_v3_pate_t {
 #define FSCR_IC_TM          5
 #define FSCR_IC_EBB         7
 #define FSCR_IC_TAR         8
+#define FSCR_IC_SCV        12
 
 /* Exception state register bits definition                                  */
 #define ESR_PIL   PPC_BIT(36) /* Illegal Instruction                    */
@@ -551,6 +554,8 @@  enum {
     POWERPC_FLAG_VSX      = 0x00080000,
     /* Has Transaction Memory (ISA 2.07)                                     */
     POWERPC_FLAG_TM       = 0x00100000,
+    /* Has SCV (ISA 3.00)                                                    */
+    POWERPC_FLAG_SCV      = 0x00200000,
 };
 
 /*****************************************************************************/
diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c
index 81ee19ebae..73b5c28d03 100644
--- a/target/ppc/excp_helper.c
+++ b/target/ppc/excp_helper.c
@@ -67,6 +67,18 @@  static inline void dump_syscall(CPUPPCState *env)
                   ppc_dump_gpr(env, 8), env->nip);
 }
 
+static inline void dump_syscall_vectored(CPUPPCState *env)
+{
+    qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64
+                  " r3=%016" PRIx64 " r4=%016" PRIx64 " r5=%016" PRIx64
+                  " r6=%016" PRIx64 " r7=%016" PRIx64 " r8=%016" PRIx64
+                  " nip=" TARGET_FMT_lx "\n",
+                  ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3),
+                  ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5),
+                  ppc_dump_gpr(env, 6), ppc_dump_gpr(env, 7),
+                  ppc_dump_gpr(env, 8), env->nip);
+}
+
 static inline void dump_hcall(CPUPPCState *env)
 {
     qemu_log_mask(CPU_LOG_INT, "hypercall r3=%016" PRIx64
@@ -185,7 +197,7 @@  static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     CPUState *cs = CPU(cpu);
     CPUPPCState *env = &cpu->env;
     target_ulong msr, new_msr, vector;
-    int srr0, srr1, asrr0, asrr1, lev, ail;
+    int srr0, srr1, asrr0, asrr1, lev = -1, ail;
     bool lpes0;
 
     qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
@@ -421,6 +433,13 @@  static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
             new_msr |= (target_ulong)MSR_HVB;
         }
         break;
+    case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception                     */
+        lev = env->error_code;
+        dump_syscall_vectored(env);
+        env->nip += 4;
+        new_msr |= env->msr & ((target_ulong)1 << MSR_EE);
+        new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+        break;
     case POWERPC_EXCP_FPU:       /* Floating-point unavailable exception     */
     case POWERPC_EXCP_APU:       /* Auxiliary processor unavailable          */
     case POWERPC_EXCP_DECR:      /* Decrementer exception                    */
@@ -723,12 +742,6 @@  static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         break;
     }
 
-    /* Save PC */
-    env->spr[srr0] = env->nip;
-
-    /* Save MSR */
-    env->spr[srr1] = msr;
-
     /* Sanity check */
     if (!(env->msr_mask & MSR_HVB)) {
         if (new_msr & MSR_HVB) {
@@ -741,14 +754,6 @@  static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         }
     }
 
-    /* If any alternate SRR register are defined, duplicate saved values */
-    if (asrr0 != -1) {
-        env->spr[asrr0] = env->spr[srr0];
-    }
-    if (asrr1 != -1) {
-        env->spr[asrr1] = env->spr[srr1];
-    }
-
     /*
      * Sort out endianness of interrupt, this differs depending on the
      * CPU, the HV mode, etc...
@@ -783,14 +788,6 @@  static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     }
 #endif
 
-    /* Jump to handler */
-    vector = env->excp_vectors[excp];
-    if (vector == (target_ulong)-1ULL) {
-        cpu_abort(cs, "Raised an exception without defined vector %d\n",
-                  excp);
-    }
-    vector |= env->excp_prefix;
-
     /*
      * AIL only works if there is no HV transition and we are running
      * with translations enabled
@@ -799,10 +796,21 @@  static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
         ((new_msr & MSR_HVB) && !(msr & MSR_HVB))) {
         ail = 0;
     }
-    /* Handle AIL */
-    if (ail) {
-        new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
-        vector |= ppc_excp_vector_offset(cs, ail);
+
+    vector = env->excp_vectors[excp];
+    if (vector == (target_ulong)-1ULL) {
+        cpu_abort(cs, "Raised an exception without defined vector %d\n",
+                  excp);
+    }
+
+    vector |= env->excp_prefix;
+
+    /* If any alternate SRR register are defined, duplicate saved values */
+    if (asrr0 != -1) {
+        env->spr[asrr0] = env->nip;
+    }
+    if (asrr1 != -1) {
+        env->spr[asrr1] = msr;
     }
 
 #if defined(TARGET_PPC64)
@@ -822,6 +830,37 @@  static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
     }
 #endif
 
+    if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
+        /* Save PC */
+        env->spr[srr0] = env->nip;
+
+        /* Save MSR */
+        env->spr[srr1] = msr;
+
+        /* Handle AIL */
+        if (ail) {
+            new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
+            vector |= ppc_excp_vector_offset(cs, ail);
+        }
+
+#if defined(TARGET_PPC64)
+    } else {
+        /* scv AIL is a little different */
+        if (ail) {
+            new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
+        }
+        if (ail == AIL_C000_0000_0000_4000) {
+            vector |= 0xc000000000003000ull;
+        } else {
+            vector |= 0x0000000000017000ull;
+        }
+        vector += lev * 0x20;
+
+        env->lr = env->nip;
+        env->ctr = msr;
+#endif
+    }
+
     powerpc_set_excp_state(cpu, vector, new_msr);
 }
 
@@ -1162,6 +1201,11 @@  void helper_rfid(CPUPPCState *env)
     do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]);
 }
 
+void helper_rfscv(CPUPPCState *env)
+{
+    do_rfi(env, env->lr, env->ctr);
+}
+
 void helper_hrfid(CPUPPCState *env)
 {
     do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index cfb4c07085..ee1498050d 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -15,6 +15,7 @@  DEF_HELPER_1(rfmci, void, env)
 #if defined(TARGET_PPC64)
 DEF_HELPER_2(pminsn, void, env, i32)
 DEF_HELPER_1(rfid, void, env)
+DEF_HELPER_1(rfscv, void, env)
 DEF_HELPER_1(hrfid, void, env)
 DEF_HELPER_2(store_lpcr, void, env, tl)
 DEF_HELPER_2(store_pcr, void, env, tl)
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 127c82a24e..eb0ddba850 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -173,6 +173,7 @@  struct DisasContext {
     bool vsx_enabled;
     bool spe_enabled;
     bool tm_enabled;
+    bool scv_enabled;
     bool gtse;
     ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
     int singlestep_enabled;
@@ -4012,6 +4013,22 @@  static void gen_rfid(DisasContext *ctx)
 #endif
 }
 
+static void gen_rfscv(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+    GEN_PRIV;
+#else
+    /* Restore CPU state */
+    CHK_SV;
+    if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
+        gen_io_start();
+    }
+    gen_update_cfar(ctx, ctx->base.pc_next - 4);
+    gen_helper_rfscv(cpu_env);
+    gen_sync_exception(ctx);
+#endif
+}
+
 static void gen_hrfid(DisasContext *ctx)
 {
 #if defined(CONFIG_USER_ONLY)
@@ -4030,6 +4047,7 @@  static void gen_hrfid(DisasContext *ctx)
 #define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL_USER
 #else
 #define POWERPC_SYSCALL POWERPC_EXCP_SYSCALL
+#define POWERPC_SYSCALL_VECTORED POWERPC_EXCP_SYSCALL_VECTORED
 #endif
 static void gen_sc(DisasContext *ctx)
 {
@@ -4039,6 +4057,21 @@  static void gen_sc(DisasContext *ctx)
     gen_exception_err(ctx, POWERPC_SYSCALL, lev);
 }
 
+#if defined(TARGET_PPC64)
+static void gen_scv(DisasContext *ctx)
+{
+    uint32_t lev;
+
+    if (unlikely(!ctx->scv_enabled)) {
+        gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_SCV);
+        return;
+    }
+
+    lev = (ctx->opcode >> 5) & 0x7F;
+    gen_exception_err(ctx, POWERPC_SYSCALL_VECTORED, lev);
+}
+#endif
+
 /***                                Trap                                   ***/
 
 /* Check for unconditional traps (always or never) */
@@ -7019,6 +7052,10 @@  GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER),
 GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW),
 #if defined(TARGET_PPC64)
 GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B),
+/* Top bit of opc2 corresponds with low bit of LEV, so use two handlers */
+GEN_HANDLER_E(scv, 0x11, 0x10, 0xFF, 0x03FFF01E, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER_E(scv, 0x11, 0x00, 0xFF, 0x03FFF01E, PPC_NONE, PPC2_ISA300),
+GEN_HANDLER_E(rfscv, 0x13, 0x12, 0x02, 0x03FF8001, PPC_NONE, PPC2_ISA300),
 GEN_HANDLER_E(stop, 0x13, 0x12, 0x0b, 0x03FFF801, PPC_NONE, PPC2_ISA300),
 GEN_HANDLER_E(doze, 0x13, 0x12, 0x0c, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
 GEN_HANDLER_E(nap, 0x13, 0x12, 0x0d, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
@@ -7026,7 +7063,9 @@  GEN_HANDLER_E(sleep, 0x13, 0x12, 0x0e, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
 GEN_HANDLER_E(rvwinkle, 0x13, 0x12, 0x0f, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
 GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H),
 #endif
-GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFF01D, PPC_FLOW),
+/* Top bit of opc2 corresponds with low bit of LEV, so use two handlers */
+GEN_HANDLER(sc, 0x11, 0x11, 0xFF, 0x03FFF01D, PPC_FLOW),
+GEN_HANDLER(sc, 0x11, 0x01, 0xFF, 0x03FFF01D, PPC_FLOW),
 GEN_HANDLER(tw, 0x1F, 0x04, 0x00, 0x00000001, PPC_FLOW),
 GEN_HANDLER(twi, 0x03, 0xFF, 0xFF, 0x00000000, PPC_FLOW),
 #if defined(TARGET_PPC64)
@@ -7805,6 +7844,11 @@  static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
     } else {
         ctx->vsx_enabled = false;
     }
+    if ((env->flags & POWERPC_FLAG_SCV) && (env->spr[SPR_FSCR] & (1ull << FSCR_SCV))) {
+        ctx->scv_enabled = true;
+    } else {
+        ctx->scv_enabled = false;
+    }
 #if defined(TARGET_PPC64)
     if ((env->flags & POWERPC_FLAG_TM) && msr_tm) {
         ctx->tm_enabled = !!msr_tm;
diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c
index 0ae145e18d..15025cd5ea 100644
--- a/target/ppc/translate_init.inc.c
+++ b/target/ppc/translate_init.inc.c
@@ -3382,6 +3382,7 @@  static void init_excp_POWER9(CPUPPCState *env)
 
 #if !defined(CONFIG_USER_ONLY)
     env->excp_vectors[POWERPC_EXCP_HVIRT]    = 0x00000EA0;
+    env->excp_vectors[POWERPC_EXCP_SYSCALL_VECTORED] = 0x00000000;
 #endif
 }
 
@@ -9030,7 +9031,7 @@  POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data)
     pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE |
                  POWERPC_FLAG_BE | POWERPC_FLAG_PMM |
                  POWERPC_FLAG_BUS_CLK | POWERPC_FLAG_CFAR |
-                 POWERPC_FLAG_VSX | POWERPC_FLAG_TM;
+                 POWERPC_FLAG_VSX | POWERPC_FLAG_TM | POWERPC_FLAG_SCV;
     pcc->l1_dcache_size = 0x8000;
     pcc->l1_icache_size = 0x8000;
     pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_lpcr;