| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- /* SPDX-License-Identifier: GPL-2.0-only */
- /*
- * Copyright (C) 2023 ARM Ltd.
- */
- #ifndef __ASM_GCS_H
- #define __ASM_GCS_H
- #include <asm/types.h>
- #include <asm/uaccess.h>
- struct kernel_clone_args;
- struct ksignal;
- static inline void gcsb_dsync(void)
- {
- asm volatile(".inst 0xd503227f" : : : "memory");
- }
- static inline void gcsstr(u64 *addr, u64 val)
- {
- register u64 *_addr __asm__ ("x0") = addr;
- register long _val __asm__ ("x1") = val;
- /* GCSSTTR x1, [x0] */
- asm volatile(
- ".inst 0xd91f1c01\n"
- :
- : "rZ" (_val), "r" (_addr)
- : "memory");
- }
- static inline void gcsss1(u64 Xt)
- {
- asm volatile (
- "sys #3, C7, C7, #2, %0\n"
- :
- : "rZ" (Xt)
- : "memory");
- }
- static inline u64 gcsss2(void)
- {
- u64 Xt;
- asm volatile(
- "SYSL %0, #3, C7, C7, #3\n"
- : "=r" (Xt)
- :
- : "memory");
- return Xt;
- }
- #define PR_SHADOW_STACK_SUPPORTED_STATUS_MASK \
- (PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE | PR_SHADOW_STACK_PUSH)
- #ifdef CONFIG_ARM64_GCS
- static inline bool task_gcs_el0_enabled(struct task_struct *task)
- {
- return task->thread.gcs_el0_mode & PR_SHADOW_STACK_ENABLE;
- }
- void gcs_set_el0_mode(struct task_struct *task);
- void gcs_free(struct task_struct *task);
- void gcs_preserve_current_state(void);
- unsigned long gcs_alloc_thread_stack(struct task_struct *tsk,
- const struct kernel_clone_args *args);
- static inline int gcs_check_locked(struct task_struct *task,
- unsigned long new_val)
- {
- unsigned long cur_val = task->thread.gcs_el0_mode;
- cur_val &= task->thread.gcs_el0_locked;
- new_val &= task->thread.gcs_el0_locked;
- if (cur_val != new_val)
- return -EBUSY;
- return 0;
- }
- static inline int gcssttr(unsigned long __user *addr, unsigned long val)
- {
- register unsigned long __user *_addr __asm__ ("x0") = addr;
- register unsigned long _val __asm__ ("x1") = val;
- int err = 0;
- /* GCSSTTR x1, [x0] */
- asm volatile(
- "1: .inst 0xd91f1c01\n"
- "2: \n"
- _ASM_EXTABLE_UACCESS_ERR(1b, 2b, %w0)
- : "+r" (err)
- : "rZ" (_val), "r" (_addr)
- : "memory");
- return err;
- }
- static inline void put_user_gcs(unsigned long val, unsigned long __user *addr,
- int *err)
- {
- int ret;
- if (!access_ok((char __user *)addr, sizeof(u64))) {
- *err = -EFAULT;
- return;
- }
- uaccess_ttbr0_enable();
- ret = gcssttr(addr, val);
- if (ret != 0)
- *err = ret;
- uaccess_ttbr0_disable();
- }
- static inline void push_user_gcs(unsigned long val, int *err)
- {
- u64 gcspr = read_sysreg_s(SYS_GCSPR_EL0);
- gcspr -= sizeof(u64);
- put_user_gcs(val, (unsigned long __user *)gcspr, err);
- if (!*err)
- write_sysreg_s(gcspr, SYS_GCSPR_EL0);
- }
- /*
- * Unlike put/push_user_gcs() above, get/pop_user_gsc() doesn't
- * validate the GCS permission is set on the page being read. This
- * differs from how the hardware works when it consumes data stored at
- * GCSPR. Callers should ensure this is acceptable.
- */
- static inline u64 get_user_gcs(unsigned long __user *addr, int *err)
- {
- unsigned long ret;
- u64 load = 0;
- /* Ensure previous GCS operation are visible before we read the page */
- gcsb_dsync();
- ret = copy_from_user(&load, addr, sizeof(load));
- if (ret != 0)
- *err = ret;
- return load;
- }
- static inline u64 pop_user_gcs(int *err)
- {
- u64 gcspr = read_sysreg_s(SYS_GCSPR_EL0);
- u64 read_val;
- read_val = get_user_gcs((__force unsigned long __user *)gcspr, err);
- if (!*err)
- write_sysreg_s(gcspr + sizeof(u64), SYS_GCSPR_EL0);
- return read_val;
- }
- #else
- static inline bool task_gcs_el0_enabled(struct task_struct *task)
- {
- return false;
- }
- static inline void gcs_set_el0_mode(struct task_struct *task) { }
- static inline void gcs_free(struct task_struct *task) { }
- static inline void gcs_preserve_current_state(void) { }
- static inline void put_user_gcs(unsigned long val, unsigned long __user *addr,
- int *err) { }
- static inline void push_user_gcs(unsigned long val, int *err) { }
- static inline unsigned long gcs_alloc_thread_stack(struct task_struct *tsk,
- const struct kernel_clone_args *args)
- {
- return -ENOTSUPP;
- }
- static inline int gcs_check_locked(struct task_struct *task,
- unsigned long new_val)
- {
- return 0;
- }
- static inline u64 get_user_gcs(unsigned long __user *addr, int *err)
- {
- *err = -EFAULT;
- return 0;
- }
- static inline u64 pop_user_gcs(int *err)
- {
- return 0;
- }
- #endif
- #endif
|