s390/kvm: integrate HANDLE_SIE_INTERCEPT into cleanup_critical
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Mon, 22 Jun 2015 15:26:40 +0000 (17:26 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 22 Jul 2015 07:58:03 +0000 (09:58 +0200)
Currently there are two mechanisms to deal with cleanup work due to
interrupts. The HANDLE_SIE_INTERCEPT macro is used to undo the changes
required to enter SIE in sie64a. If the SIE instruction causes a program
check, or an asynchronous interrupt is received the HANDLE_SIE_INTERCEPT
code forwards the program execution to sie_exit.

All the other critical sections in entry.S are handled by the code in
cleanup_critical that is called by the SWITCH_ASYNC macro.

Move the sie64a function to the beginning of the critical section and
add the code from HANDLE_SIE_INTERCEPT to cleanup_critical. Add a special
case for the sie64a cleanup to the program check handler.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/kernel/entry.S

index 391db64..9406e7a 100644 (file)
@@ -75,31 +75,6 @@ _PIF_WORK    = (_PIF_PER_TRAP)
 #endif
        .endm
 
-       .macro LPP newpp
-#if IS_ENABLED(CONFIG_KVM)
-       tm      __LC_MACHINE_FLAGS+6,0x20       # MACHINE_FLAG_LPP
-       jz      .+8
-       .insn   s,0xb2800000,\newpp
-#endif
-       .endm
-
-       .macro  HANDLE_SIE_INTERCEPT scratch,reason
-#if IS_ENABLED(CONFIG_KVM)
-       tmhh    %r8,0x0001              # interrupting from user ?
-       jnz     .+62
-       lgr     \scratch,%r9
-       slg     \scratch,BASED(.Lsie_critical)
-       clg     \scratch,BASED(.Lsie_critical_length)
-       jhe     .+42
-       lg      %r14,__SF_EMPTY(%r15)           # get control block pointer
-       LPP     __SF_EMPTY+16(%r15)             # set host id
-       ni      __SIE_PROG0C+3(%r14),0xfe       # no longer in SIE
-       lctlg   %c1,%c1,__LC_USER_ASCE          # load primary asce
-       larl    %r9,sie_exit                    # skip forward to sie_exit
-       mvi     __SF_EMPTY+31(%r15),\reason     # set exit reason
-#endif
-       .endm
-
        .macro  CHECK_STACK stacksize,savearea
 #ifdef CONFIG_CHECK_STACK
        tml     %r15,\stacksize - CONFIG_STACK_GUARD
@@ -192,6 +167,70 @@ ENTRY(__switch_to)
        br      %r14
 
 .L__critical_start:
+
+#if IS_ENABLED(CONFIG_KVM)
+/*
+ * sie64a calling convention:
+ * %r2 pointer to sie control block
+ * %r3 guest register save area
+ */
+ENTRY(sie64a)
+       stmg    %r6,%r14,__SF_GPRS(%r15)        # save kernel registers
+       stg     %r2,__SF_EMPTY(%r15)            # save control block pointer
+       stg     %r3,__SF_EMPTY+8(%r15)          # save guest register save area
+       xc      __SF_EMPTY+16(16,%r15),__SF_EMPTY+16(%r15) # host id & reason
+       tm      __LC_CPU_FLAGS+7,_CIF_FPU       # load guest fp/vx registers ?
+       jno     .Lsie_load_guest_gprs
+       lg      %r12,__LC_THREAD_INFO           # load fp/vx regs save area
+       brasl   %r14,load_fpu_regs              # load guest fp/vx regs
+.Lsie_load_guest_gprs:
+       lmg     %r0,%r13,0(%r3)                 # load guest gprs 0-13
+       lg      %r14,__LC_GMAP                  # get gmap pointer
+       ltgr    %r14,%r14
+       jz      .Lsie_gmap
+       lctlg   %c1,%c1,__GMAP_ASCE(%r14)       # load primary asce
+.Lsie_gmap:
+       lg      %r14,__SF_EMPTY(%r15)           # get control block pointer
+       oi      __SIE_PROG0C+3(%r14),1          # we are going into SIE now
+       tm      __SIE_PROG20+3(%r14),3          # last exit...
+       jnz     .Lsie_skip
+       tm      __LC_CPU_FLAGS+7,_CIF_FPU
+       jo      .Lsie_skip                      # exit if fp/vx regs changed
+       tm      __LC_MACHINE_FLAGS+6,0x20       # MACHINE_FLAG_LPP
+       jz      .Lsie_enter
+       .insn   s,0xb2800000,__SF_EMPTY(%r15)   # set guest id
+.Lsie_enter:
+       sie     0(%r14)
+       tm      __LC_MACHINE_FLAGS+6,0x20       # MACHINE_FLAG_LPP
+       jz      .Lsie_skip
+       .insn   s,0xb2800000,__SF_EMPTY+16(%r15)# set host id
+.Lsie_skip:
+       ni      __SIE_PROG0C+3(%r14),0xfe       # no longer in SIE
+       lctlg   %c1,%c1,__LC_USER_ASCE          # load primary asce
+.Lsie_done:
+# some program checks are suppressing. C code (e.g. do_protection_exception)
+# will rewind the PSW by the ILC, which is 4 bytes in case of SIE. Other
+# instructions between sie64a and .Lsie_done should not cause program
+# interrupts. So lets use a nop (47 00 00 00) as a landing pad.
+# See also .Lcleanup_sie
+.Lrewind_pad:
+       nop     0
+       .globl sie_exit
+sie_exit:
+       lg      %r14,__SF_EMPTY+8(%r15)         # load guest register save area
+       stmg    %r0,%r13,0(%r14)                # save guest gprs 0-13
+       lmg     %r6,%r14,__SF_GPRS(%r15)        # restore kernel registers
+       lg      %r2,__SF_EMPTY+24(%r15)         # return exit reason code
+       br      %r14
+.Lsie_fault:
+       lghi    %r14,-EFAULT
+       stg     %r14,__SF_EMPTY+24(%r15)        # set exit reason code
+       j       sie_exit
+
+       EX_TABLE(.Lrewind_pad,.Lsie_fault)
+       EX_TABLE(sie_exit,.Lsie_fault)
+#endif
+
 /*
  * SVC interrupt handler routine. System calls are synchronous events and
  * are executed with interrupts enabled.
@@ -411,26 +450,33 @@ ENTRY(pgm_check_handler)
        lg      %r12,__LC_THREAD_INFO
        larl    %r13,cleanup_critical
        lmg     %r8,%r9,__LC_PGM_OLD_PSW
-       HANDLE_SIE_INTERCEPT %r14,1
        tmhh    %r8,0x0001              # test problem state bit
-       jnz     1f                      # -> fault in user space
-       tmhh    %r8,0x4000              # PER bit set in old PSW ?
-       jnz     0f                      # -> enabled, can't be a double fault
+       jnz     2f                      # -> fault in user space
+#if IS_ENABLED(CONFIG_KVM)
+       # cleanup critical section for sie64a
+       lgr     %r14,%r9
+       slg     %r14,BASED(.Lsie_critical_start)
+       clg     %r14,BASED(.Lsie_critical_length)
+       jhe     0f
+       brasl   %r14,.Lcleanup_sie
+#endif
+0:     tmhh    %r8,0x4000              # PER bit set in old PSW ?
+       jnz     1f                      # -> enabled, can't be a double fault
        tm      __LC_PGM_ILC+3,0x80     # check for per exception
        jnz     .Lpgm_svcper            # -> single stepped svc
-0:     CHECK_STACK STACK_SIZE,__LC_SAVE_AREA_SYNC
+1:     CHECK_STACK STACK_SIZE,__LC_SAVE_AREA_SYNC
        aghi    %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
-       j       2f
-1:     UPDATE_VTIME %r14,__LC_SYNC_ENTER_TIMER
+       j       3f
+2:     UPDATE_VTIME %r14,__LC_SYNC_ENTER_TIMER
        LAST_BREAK %r14
        lg      %r15,__LC_KERNEL_STACK
        lg      %r14,__TI_task(%r12)
        aghi    %r14,__TASK_thread      # pointer to thread_struct
        lghi    %r13,__LC_PGM_TDB
        tm      __LC_PGM_ILC+2,0x02     # check for transaction abort
-       jz      2f
+       jz      3f
        mvc     __THREAD_trap_tdb(256,%r14),0(%r13)
-2:     la      %r11,STACK_FRAME_OVERHEAD(%r15)
+3:     la      %r11,STACK_FRAME_OVERHEAD(%r15)
        stmg    %r0,%r7,__PT_R0(%r11)
        mvc     __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC
        stmg    %r8,%r9,__PT_PSW(%r11)
@@ -439,14 +485,14 @@ ENTRY(pgm_check_handler)
        xc      __PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
        stg     %r10,__PT_ARGS(%r11)
        tm      __LC_PGM_ILC+3,0x80     # check for per exception
-       jz      0f
+       jz      4f
        tmhh    %r8,0x0001              # kernel per event ?
        jz      .Lpgm_kprobe
        oi      __PT_FLAGS+7(%r11),_PIF_PER_TRAP
        mvc     __THREAD_per_address(8,%r14),__LC_PER_ADDRESS
        mvc     __THREAD_per_cause(2,%r14),__LC_PER_CODE
        mvc     __THREAD_per_paid(1,%r14),__LC_PER_ACCESS_ID
-0:     REENABLE_IRQS
+4:     REENABLE_IRQS
        xc      __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
        larl    %r1,pgm_check_table
        llgh    %r10,__PT_INT_CODE+2(%r11)
@@ -489,7 +535,6 @@ ENTRY(io_int_handler)
        lg      %r12,__LC_THREAD_INFO
        larl    %r13,cleanup_critical
        lmg     %r8,%r9,__LC_IO_OLD_PSW
-       HANDLE_SIE_INTERCEPT %r14,2
        SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT
        tmhh    %r8,0x0001              # interrupting from user?
        jz      .Lio_skip
@@ -667,7 +712,6 @@ ENTRY(ext_int_handler)
        lg      %r12,__LC_THREAD_INFO
        larl    %r13,cleanup_critical
        lmg     %r8,%r9,__LC_EXT_OLD_PSW
-       HANDLE_SIE_INTERCEPT %r14,3
        SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT
        tmhh    %r8,0x0001              # interrupting from user ?
        jz      .Lext_skip
@@ -832,7 +876,6 @@ ENTRY(mcck_int_handler)
        lg      %r12,__LC_THREAD_INFO
        larl    %r13,cleanup_critical
        lmg     %r8,%r9,__LC_MCK_OLD_PSW
-       HANDLE_SIE_INTERCEPT %r14,4
        tm      __LC_MCCK_CODE,0x80     # system damage?
        jo      .Lmcck_panic            # yes -> rest of mcck code invalid
        lghi    %r14,__LC_CPU_TIMER_SAVE_AREA
@@ -947,26 +990,13 @@ stack_overflow:
        jg      kernel_stack_overflow
 #endif
 
-       .align  8
-.Lcleanup_table:
-       .quad   system_call
-       .quad   .Lsysc_do_svc
-       .quad   .Lsysc_tif
-       .quad   .Lsysc_restore
-       .quad   .Lsysc_done
-       .quad   .Lio_tif
-       .quad   .Lio_restore
-       .quad   .Lio_done
-       .quad   psw_idle
-       .quad   .Lpsw_idle_end
-       .quad   save_fpu_regs
-       .quad   .Lsave_fpu_regs_end
-       .quad   load_fpu_regs
-       .quad   .Lload_fpu_regs_end
-       .quad   __ctl_set_vx
-       .quad   .L__ctl_set_vx_end
-
 cleanup_critical:
+#if IS_ENABLED(CONFIG_KVM)
+       clg     %r9,BASED(.Lcleanup_table_sie)  # .Lsie_gmap
+       jl      0f
+       clg     %r9,BASED(.Lcleanup_table_sie+8)# .Lsie_done
+       jl      .Lcleanup_sie
+#endif
        clg     %r9,BASED(.Lcleanup_table)      # system_call
        jl      0f
        clg     %r9,BASED(.Lcleanup_table+8)    # .Lsysc_do_svc
@@ -1001,6 +1031,40 @@ cleanup_critical:
        jl      .Lcleanup___ctl_set_vx
 0:     br      %r14
 
+       .align  8
+.Lcleanup_table:
+       .quad   system_call
+       .quad   .Lsysc_do_svc
+       .quad   .Lsysc_tif
+       .quad   .Lsysc_restore
+       .quad   .Lsysc_done
+       .quad   .Lio_tif
+       .quad   .Lio_restore
+       .quad   .Lio_done
+       .quad   psw_idle
+       .quad   .Lpsw_idle_end
+       .quad   save_fpu_regs
+       .quad   .Lsave_fpu_regs_end
+       .quad   load_fpu_regs
+       .quad   .Lload_fpu_regs_end
+       .quad   __ctl_set_vx
+       .quad   .L__ctl_set_vx_end
+
+#if IS_ENABLED(CONFIG_KVM)
+.Lcleanup_table_sie:
+       .quad   .Lsie_gmap
+       .quad   .Lsie_done
+
+.Lcleanup_sie:
+       lg      %r9,__SF_EMPTY(%r15)            # get control block pointer
+       tm      __LC_MACHINE_FLAGS+6,0x20       # MACHINE_FLAG_LPP
+       jz      0f
+       .insn   s,0xb2800000,__SF_EMPTY+16(%r15)# set host id
+0:     ni      __SIE_PROG0C+3(%r9),0xfe        # no longer in SIE
+       lctlg   %c1,%c1,__LC_USER_ASCE          # load primary asce
+       larl    %r9,sie_exit                    # skip forward to sie_exit
+       br      %r14
+#endif
 
 .Lcleanup_system_call:
        # check if stpt has been executed
@@ -1274,70 +1338,11 @@ cleanup_critical:
        .quad   .L__critical_start
 .Lcritical_length:
        .quad   .L__critical_end - .L__critical_start
-
-
 #if IS_ENABLED(CONFIG_KVM)
-/*
- * sie64a calling convention:
- * %r2 pointer to sie control block
- * %r3 guest register save area
- */
-ENTRY(sie64a)
-       stmg    %r6,%r14,__SF_GPRS(%r15)        # save kernel registers
-       stg     %r2,__SF_EMPTY(%r15)            # save control block pointer
-       stg     %r3,__SF_EMPTY+8(%r15)          # save guest register save area
-       xc      __SF_EMPTY+16(16,%r15),__SF_EMPTY+16(%r15) # host id & reason
-       tm      __LC_CPU_FLAGS+7,_CIF_FPU       # load guest fp/vx registers ?
-       jno     .Lsie_load_guest_gprs
-       lg      %r12,__LC_THREAD_INFO           # load fp/vx regs save area
-       brasl   %r14,load_fpu_regs              # load guest fp/vx regs
-.Lsie_load_guest_gprs:
-       lmg     %r0,%r13,0(%r3)                 # load guest gprs 0-13
-       lg      %r14,__LC_GMAP                  # get gmap pointer
-       ltgr    %r14,%r14
-       jz      .Lsie_gmap
-       lctlg   %c1,%c1,__GMAP_ASCE(%r14)       # load primary asce
-.Lsie_gmap:
-       lg      %r14,__SF_EMPTY(%r15)           # get control block pointer
-       oi      __SIE_PROG0C+3(%r14),1          # we are going into SIE now
-       tm      __SIE_PROG20+3(%r14),3          # last exit...
-       jnz     .Lsie_skip
-       tm      __LC_CPU_FLAGS+7,_CIF_FPU
-       jo      .Lsie_skip                      # exit if fp/vx regs changed
-       LPP     __SF_EMPTY(%r15)                # set guest id
-       sie     0(%r14)
-       LPP     __SF_EMPTY+16(%r15)             # set host id
-.Lsie_skip:
-       ni      __SIE_PROG0C+3(%r14),0xfe       # no longer in SIE
-       lctlg   %c1,%c1,__LC_USER_ASCE          # load primary asce
-.Lsie_done:
-# some program checks are suppressing. C code (e.g. do_protection_exception)
-# will rewind the PSW by the ILC, which is 4 bytes in case of SIE. Other
-# instructions between sie64a and .Lsie_done should not cause program
-# interrupts. So lets use a nop (47 00 00 00) as a landing pad.
-# See also HANDLE_SIE_INTERCEPT
-.Lrewind_pad:
-       nop     0
-       .globl sie_exit
-sie_exit:
-       lg      %r14,__SF_EMPTY+8(%r15)         # load guest register save area
-       stmg    %r0,%r13,0(%r14)                # save guest gprs 0-13
-       lmg     %r6,%r14,__SF_GPRS(%r15)        # restore kernel registers
-       lg      %r2,__SF_EMPTY+24(%r15)         # return exit reason code
-       br      %r14
-.Lsie_fault:
-       lghi    %r14,-EFAULT
-       stg     %r14,__SF_EMPTY+24(%r15)        # set exit reason code
-       j       sie_exit
-
-       .align  8
-.Lsie_critical:
+.Lsie_critical_start:
        .quad   .Lsie_gmap
 .Lsie_critical_length:
        .quad   .Lsie_done - .Lsie_gmap
-
-       EX_TABLE(.Lrewind_pad,.Lsie_fault)
-       EX_TABLE(sie_exit,.Lsie_fault)
 #endif
 
        .section .rodata, "a"