diff options
Diffstat (limited to 'arch/x86/kernel/fpu/xstate.c')
-rw-r--r-- | arch/x86/kernel/fpu/xstate.c | 138 |
1 files changed, 17 insertions, 121 deletions
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c index 680049aa4593..01567aa87503 100644 --- a/arch/x86/kernel/fpu/xstate.c +++ b/arch/x86/kernel/fpu/xstate.c | |||
@@ -866,105 +866,17 @@ const void *get_xsave_field_ptr(int xsave_state) | |||
866 | return get_xsave_addr(&fpu->state.xsave, xsave_state); | 866 | return get_xsave_addr(&fpu->state.xsave, xsave_state); |
867 | } | 867 | } |
868 | 868 | ||
869 | |||
870 | /* | ||
871 | * Set xfeatures (aka XSTATE_BV) bit for a feature that we want | ||
872 | * to take out of its "init state". This will ensure that an | ||
873 | * XRSTOR actually restores the state. | ||
874 | */ | ||
875 | static void fpu__xfeature_set_non_init(struct xregs_state *xsave, | ||
876 | int xstate_feature_mask) | ||
877 | { | ||
878 | xsave->header.xfeatures |= xstate_feature_mask; | ||
879 | } | ||
880 | |||
881 | /* | ||
882 | * This function is safe to call whether the FPU is in use or not. | ||
883 | * | ||
884 | * Note that this only works on the current task. | ||
885 | * | ||
886 | * Inputs: | ||
887 | * @xsave_state: state which is defined in xsave.h (e.g. XFEATURE_MASK_FP, | ||
888 | * XFEATURE_MASK_SSE, etc...) | ||
889 | * @xsave_state_ptr: a pointer to a copy of the state that you would | ||
890 | * like written in to the current task's FPU xsave state. This pointer | ||
891 | * must not be located in the current tasks's xsave area. | ||
892 | * Output: | ||
893 | * address of the state in the xsave area or NULL if the state | ||
894 | * is not present or is in its 'init state'. | ||
895 | */ | ||
896 | static void fpu__xfeature_set_state(int xstate_feature_mask, | ||
897 | void *xstate_feature_src, size_t len) | ||
898 | { | ||
899 | struct xregs_state *xsave = ¤t->thread.fpu.state.xsave; | ||
900 | struct fpu *fpu = ¤t->thread.fpu; | ||
901 | void *dst; | ||
902 | |||
903 | if (!boot_cpu_has(X86_FEATURE_XSAVE)) { | ||
904 | WARN_ONCE(1, "%s() attempted with no xsave support", __func__); | ||
905 | return; | ||
906 | } | ||
907 | |||
908 | /* | ||
909 | * Tell the FPU code that we need the FPU state to be in | ||
910 | * 'fpu' (not in the registers), and that we need it to | ||
911 | * be stable while we write to it. | ||
912 | */ | ||
913 | fpu__current_fpstate_write_begin(); | ||
914 | |||
915 | /* | ||
916 | * This method *WILL* *NOT* work for compact-format | ||
917 | * buffers. If the 'xstate_feature_mask' is unset in | ||
918 | * xcomp_bv then we may need to move other feature state | ||
919 | * "up" in the buffer. | ||
920 | */ | ||
921 | if (xsave->header.xcomp_bv & xstate_feature_mask) { | ||
922 | WARN_ON_ONCE(1); | ||
923 | goto out; | ||
924 | } | ||
925 | |||
926 | /* find the location in the xsave buffer of the desired state */ | ||
927 | dst = __raw_xsave_addr(&fpu->state.xsave, xstate_feature_mask); | ||
928 | |||
929 | /* | ||
930 | * Make sure that the pointer being passed in did not | ||
931 | * come from the xsave buffer itself. | ||
932 | */ | ||
933 | WARN_ONCE(xstate_feature_src == dst, "set from xsave buffer itself"); | ||
934 | |||
935 | /* put the caller-provided data in the location */ | ||
936 | memcpy(dst, xstate_feature_src, len); | ||
937 | |||
938 | /* | ||
939 | * Mark the xfeature so that the CPU knows there is state | ||
940 | * in the buffer now. | ||
941 | */ | ||
942 | fpu__xfeature_set_non_init(xsave, xstate_feature_mask); | ||
943 | out: | ||
944 | /* | ||
945 | * We are done writing to the 'fpu'. Reenable preeption | ||
946 | * and (possibly) move the fpstate back in to the fpregs. | ||
947 | */ | ||
948 | fpu__current_fpstate_write_end(); | ||
949 | } | ||
950 | |||
951 | #define NR_VALID_PKRU_BITS (CONFIG_NR_PROTECTION_KEYS * 2) | 869 | #define NR_VALID_PKRU_BITS (CONFIG_NR_PROTECTION_KEYS * 2) |
952 | #define PKRU_VALID_MASK (NR_VALID_PKRU_BITS - 1) | 870 | #define PKRU_VALID_MASK (NR_VALID_PKRU_BITS - 1) |
953 | 871 | ||
954 | /* | 872 | /* |
955 | * This will go out and modify the XSAVE buffer so that PKRU is | 873 | * This will go out and modify PKRU register to set the access |
956 | * set to a particular state for access to 'pkey'. | 874 | * rights for @pkey to @init_val. |
957 | * | ||
958 | * PKRU state does affect kernel access to user memory. We do | ||
959 | * not modfiy PKRU *itself* here, only the XSAVE state that will | ||
960 | * be restored in to PKRU when we return back to userspace. | ||
961 | */ | 875 | */ |
962 | int arch_set_user_pkey_access(struct task_struct *tsk, int pkey, | 876 | int arch_set_user_pkey_access(struct task_struct *tsk, int pkey, |
963 | unsigned long init_val) | 877 | unsigned long init_val) |
964 | { | 878 | { |
965 | struct xregs_state *xsave = &tsk->thread.fpu.state.xsave; | 879 | u32 old_pkru; |
966 | struct pkru_state *old_pkru_state; | ||
967 | struct pkru_state new_pkru_state; | ||
968 | int pkey_shift = (pkey * PKRU_BITS_PER_PKEY); | 880 | int pkey_shift = (pkey * PKRU_BITS_PER_PKEY); |
969 | u32 new_pkru_bits = 0; | 881 | u32 new_pkru_bits = 0; |
970 | 882 | ||
@@ -974,6 +886,15 @@ int arch_set_user_pkey_access(struct task_struct *tsk, int pkey, | |||
974 | */ | 886 | */ |
975 | if (!boot_cpu_has(X86_FEATURE_OSPKE)) | 887 | if (!boot_cpu_has(X86_FEATURE_OSPKE)) |
976 | return -EINVAL; | 888 | return -EINVAL; |
889 | /* | ||
890 | * For most XSAVE components, this would be an arduous task: | ||
891 | * brining fpstate up to date with fpregs, updating fpstate, | ||
892 | * then re-populating fpregs. But, for components that are | ||
893 | * never lazily managed, we can just access the fpregs | ||
894 | * directly. PKRU is never managed lazily, so we can just | ||
895 | * manipulate it directly. Make sure it stays that way. | ||
896 | */ | ||
897 | WARN_ON_ONCE(!use_eager_fpu()); | ||
977 | 898 | ||
978 | /* Set the bits we need in PKRU: */ | 899 | /* Set the bits we need in PKRU: */ |
979 | if (init_val & PKEY_DISABLE_ACCESS) | 900 | if (init_val & PKEY_DISABLE_ACCESS) |
@@ -984,37 +905,12 @@ int arch_set_user_pkey_access(struct task_struct *tsk, int pkey, | |||
984 | /* Shift the bits in to the correct place in PKRU for pkey: */ | 905 | /* Shift the bits in to the correct place in PKRU for pkey: */ |
985 | new_pkru_bits <<= pkey_shift; | 906 | new_pkru_bits <<= pkey_shift; |
986 | 907 | ||
987 | /* Locate old copy of the state in the xsave buffer: */ | 908 | /* Get old PKRU and mask off any old bits in place: */ |
988 | old_pkru_state = get_xsave_addr(xsave, XFEATURE_MASK_PKRU); | 909 | old_pkru = read_pkru(); |
989 | 910 | old_pkru &= ~((PKRU_AD_BIT|PKRU_WD_BIT) << pkey_shift); | |
990 | /* | ||
991 | * When state is not in the buffer, it is in the init | ||
992 | * state, set it manually. Otherwise, copy out the old | ||
993 | * state. | ||
994 | */ | ||
995 | if (!old_pkru_state) | ||
996 | new_pkru_state.pkru = 0; | ||
997 | else | ||
998 | new_pkru_state.pkru = old_pkru_state->pkru; | ||
999 | |||
1000 | /* Mask off any old bits in place: */ | ||
1001 | new_pkru_state.pkru &= ~((PKRU_AD_BIT|PKRU_WD_BIT) << pkey_shift); | ||
1002 | |||
1003 | /* Set the newly-requested bits: */ | ||
1004 | new_pkru_state.pkru |= new_pkru_bits; | ||
1005 | |||
1006 | /* | ||
1007 | * We could theoretically live without zeroing pkru.pad. | ||
1008 | * The current XSAVE feature state definition says that | ||
1009 | * only bytes 0->3 are used. But we do not want to | ||
1010 | * chance leaking kernel stack out to userspace in case a | ||
1011 | * memcpy() of the whole xsave buffer was done. | ||
1012 | * | ||
1013 | * They're in the same cacheline anyway. | ||
1014 | */ | ||
1015 | new_pkru_state.pad = 0; | ||
1016 | 911 | ||
1017 | fpu__xfeature_set_state(XFEATURE_MASK_PKRU, &new_pkru_state, sizeof(new_pkru_state)); | 912 | /* Write old part along with new part: */ |
913 | write_pkru(old_pkru | new_pkru_bits); | ||
1018 | 914 | ||
1019 | return 0; | 915 | return 0; |
1020 | } | 916 | } |