Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1/* SPDX-License-Identifier: GPL-2.0-only */
2/*
3 * Copyright (C) 2023 ARM Ltd.
4 */
5#ifndef __ASM_GCS_H
6#define __ASM_GCS_H
7
8#include <asm/types.h>
9#include <asm/uaccess.h>
10
11struct kernel_clone_args;
12struct ksignal;
13
14static inline void gcsb_dsync(void)
15{
16 asm volatile(".inst 0xd503227f" : : : "memory");
17}
18
19static inline void gcsstr(u64 *addr, u64 val)
20{
21 register u64 *_addr __asm__ ("x0") = addr;
22 register long _val __asm__ ("x1") = val;
23
24 /* GCSSTTR x1, [x0] */
25 asm volatile(
26 ".inst 0xd91f1c01\n"
27 :
28 : "rZ" (_val), "r" (_addr)
29 : "memory");
30}
31
32static inline void gcsss1(u64 Xt)
33{
34 asm volatile (
35 "sys #3, C7, C7, #2, %0\n"
36 :
37 : "rZ" (Xt)
38 : "memory");
39}
40
41static inline u64 gcsss2(void)
42{
43 u64 Xt;
44
45 asm volatile(
46 "SYSL %0, #3, C7, C7, #3\n"
47 : "=r" (Xt)
48 :
49 : "memory");
50
51 return Xt;
52}
53
54#define PR_SHADOW_STACK_SUPPORTED_STATUS_MASK \
55 (PR_SHADOW_STACK_ENABLE | PR_SHADOW_STACK_WRITE | PR_SHADOW_STACK_PUSH)
56
57#ifdef CONFIG_ARM64_GCS
58
59static inline bool task_gcs_el0_enabled(struct task_struct *task)
60{
61 return task->thread.gcs_el0_mode & PR_SHADOW_STACK_ENABLE;
62}
63
64void gcs_set_el0_mode(struct task_struct *task);
65void gcs_free(struct task_struct *task);
66void gcs_preserve_current_state(void);
67unsigned long gcs_alloc_thread_stack(struct task_struct *tsk,
68 const struct kernel_clone_args *args);
69
70static inline int gcs_check_locked(struct task_struct *task,
71 unsigned long new_val)
72{
73 unsigned long cur_val = task->thread.gcs_el0_mode;
74
75 cur_val &= task->thread.gcs_el0_locked;
76 new_val &= task->thread.gcs_el0_locked;
77
78 if (cur_val != new_val)
79 return -EBUSY;
80
81 return 0;
82}
83
84static inline int gcssttr(unsigned long __user *addr, unsigned long val)
85{
86 register unsigned long __user *_addr __asm__ ("x0") = addr;
87 register unsigned long _val __asm__ ("x1") = val;
88 int err = 0;
89
90 /* GCSSTTR x1, [x0] */
91 asm volatile(
92 "1: .inst 0xd91f1c01\n"
93 "2: \n"
94 _ASM_EXTABLE_UACCESS_ERR(1b, 2b, %w0)
95 : "+r" (err)
96 : "rZ" (_val), "r" (_addr)
97 : "memory");
98
99 return err;
100}
101
102static inline void put_user_gcs(unsigned long val, unsigned long __user *addr,
103 int *err)
104{
105 int ret;
106
107 if (!access_ok((char __user *)addr, sizeof(u64))) {
108 *err = -EFAULT;
109 return;
110 }
111
112 uaccess_ttbr0_enable();
113 ret = gcssttr(addr, val);
114 if (ret != 0)
115 *err = ret;
116 uaccess_ttbr0_disable();
117}
118
119static inline void push_user_gcs(unsigned long val, int *err)
120{
121 u64 gcspr = read_sysreg_s(SYS_GCSPR_EL0);
122
123 gcspr -= sizeof(u64);
124 put_user_gcs(val, (unsigned long __user *)gcspr, err);
125 if (!*err)
126 write_sysreg_s(gcspr, SYS_GCSPR_EL0);
127}
128
129/*
130 * Unlike put/push_user_gcs() above, get/pop_user_gsc() doesn't
131 * validate the GCS permission is set on the page being read. This
132 * differs from how the hardware works when it consumes data stored at
133 * GCSPR. Callers should ensure this is acceptable.
134 */
135static inline u64 get_user_gcs(unsigned long __user *addr, int *err)
136{
137 unsigned long ret;
138 u64 load = 0;
139
140 /* Ensure previous GCS operation are visible before we read the page */
141 gcsb_dsync();
142 ret = copy_from_user(&load, addr, sizeof(load));
143 if (ret != 0)
144 *err = ret;
145 return load;
146}
147
148static inline u64 pop_user_gcs(int *err)
149{
150 u64 gcspr = read_sysreg_s(SYS_GCSPR_EL0);
151 u64 read_val;
152
153 read_val = get_user_gcs((__force unsigned long __user *)gcspr, err);
154 if (!*err)
155 write_sysreg_s(gcspr + sizeof(u64), SYS_GCSPR_EL0);
156
157 return read_val;
158}
159
160#else
161
162static inline bool task_gcs_el0_enabled(struct task_struct *task)
163{
164 return false;
165}
166
167static inline void gcs_set_el0_mode(struct task_struct *task) { }
168static inline void gcs_free(struct task_struct *task) { }
169static inline void gcs_preserve_current_state(void) { }
170static inline void put_user_gcs(unsigned long val, unsigned long __user *addr,
171 int *err) { }
172static inline void push_user_gcs(unsigned long val, int *err) { }
173
174static inline unsigned long gcs_alloc_thread_stack(struct task_struct *tsk,
175 const struct kernel_clone_args *args)
176{
177 return -ENOTSUPP;
178}
179static inline int gcs_check_locked(struct task_struct *task,
180 unsigned long new_val)
181{
182 return 0;
183}
184static inline u64 get_user_gcs(unsigned long __user *addr, int *err)
185{
186 *err = -EFAULT;
187 return 0;
188}
189static inline u64 pop_user_gcs(int *err)
190{
191 return 0;
192}
193
194#endif
195
196#endif