• Mark Rutland's avatar
    arm64: syscall: unmask DAIF earlier for SVCs · f130ac0a
    Mark Rutland authored
    For a number of historical reasons, when handling SVCs we don't unmask
    DAIF in el0_svc() or el0_svc_compat(), and instead do so later in
    el0_svc_common(). This is unfortunate and makes it harder to make
    changes to the DAIF management in entry-common.c as we'd like to do as
    cleanup and preparation for FEAT_NMI support. We can move the DAIF
    unmasking to entry-common.c as long as we also hoist the
    fp_user_discard() logic, as reasoned below.
    
    We converted the syscall trace logic from assembly to C in commit:
    
      f37099b6 ("arm64: convert syscall trace logic to C")
    
    ... which was intended to have no functional change, and mirrored the
    existing assembly logic to avoid the risk of any functional regression.
    
    With the logic in C, it's clear that there is currently no reason to
    unmask DAIF so late within el0_svc_common():
    
    * The thread flags are read prior to unmasking DAIF, but are not
      consumed until after DAIF is unmasked, and we don't perform a
      read-modify-write sequence of the thread flags for which we might need
      to serialize against an IPI modifying the flags. Similarly, for any
      thread flags set by other threads, whether DAIF is masked or not has
      no impact.
    
      The read_thread_flags() helpers performs a single-copy-atomic read of
      the flags, and so this can safely be moved after unmasking DAIF.
    
    * The pt_regs::orig_x0 and pt_regs::syscallno fields are neither
      consumed nor modified by the handler for any DAIF exception (e.g.
      these do not exist in the `perf_event_arm_regs` enum and are not
      sampled by perf in its IRQ handler).
    
      Thus, the manipulation of pt_regs::orig_x0 and pt_regs::syscallno can
      safely be moved after unmasking DAIF.
    
    Given the above, we can safely hoist unmasking of DAIF out of
    el0_svc_common(), and into its immediate callers: do_el0_svc() and
    do_el0_svc_compat(). Further:
    
    * In do_el0_svc(), we sample the syscall number from
      pt_regs::regs[8]. This is not modified by the handler for any DAIF
      exception, and thus can safely be moved after unmasking DAIF.
    
      As fp_user_discard() operates on the live FP/SVE/SME register state,
      this needs to occur before we clear DAIF.IF, as interrupts could
      result in preemption which would cause this state to become foreign.
      As fp_user_discard() is the first function called within do_el0_svc(),
      it has no dependency on other parts of do_el0_svc() and can be moved
      earlier so long as it is called prior to unmasking DAIF.IF.
    
    * In do_el0_svc_compat(), we sample the syscall number from
      pt_regs::regs[7]. This is not modified by the handler for any DAIF
      exception, and thus can safely be moved after unmasking DAIF.
    
      Compat threads cannot use SVE or SME, so there's no need for
      el0_svc_compat() to call fp_user_discard().
    
    Given the above, we can safely hoist the unmasking of DAIF out of
    do_el0_svc() and do_el0_svc_compat(), and into their immediate callers:
    el0_svc() and el0_svc_compat(), so long a we also hoist
    fp_user_discard() into el0_svc().
    Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
    Cc: Catalin Marinas <catalin.marinas@arm.com>
    Cc: Marc Zyngier <maz@kernel.org>
    Cc: Mark Brown <broonie@kernel.org>
    Cc: Will Deacon <will@kernel.org>
    Reviewed-by: default avatarMark Brown <broonie@kernel.org>
    Link: https://lore.kernel.org/r/20230808101148.1064172-1-mark.rutland@arm.comSigned-off-by: default avatarWill Deacon <will@kernel.org>
    f130ac0a
syscall.c 4.69 KB