1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright IBM Corp. 2016
4 * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
5 */
6
7#include <linux/cpufeature.h>
8#include <linux/kernel.h>
9#include <linux/syscalls.h>
10#include <linux/signal.h>
11#include <linux/mm.h>
12#include <linux/slab.h>
13#include <asm/guarded_storage.h>
14#include "entry.h"
15
16void guarded_storage_release(struct task_struct *tsk)
17{
18 kfree(objp: tsk->thread.gs_cb);
19 kfree(objp: tsk->thread.gs_bc_cb);
20}
21
22static int gs_enable(void)
23{
24 struct gs_cb *gs_cb;
25
26 if (!current->thread.gs_cb) {
27 gs_cb = kzalloc(sizeof(*gs_cb), GFP_KERNEL);
28 if (!gs_cb)
29 return -ENOMEM;
30 gs_cb->gsd = 25;
31 preempt_disable();
32 local_ctl_set_bit(2, CR2_GUARDED_STORAGE_BIT);
33 load_gs_cb(gs_cb);
34 current->thread.gs_cb = gs_cb;
35 preempt_enable();
36 }
37 return 0;
38}
39
40static int gs_disable(void)
41{
42 if (current->thread.gs_cb) {
43 preempt_disable();
44 kfree(current->thread.gs_cb);
45 current->thread.gs_cb = NULL;
46 local_ctl_clear_bit(2, CR2_GUARDED_STORAGE_BIT);
47 preempt_enable();
48 }
49 return 0;
50}
51
52static int gs_set_bc_cb(struct gs_cb __user *u_gs_cb)
53{
54 struct gs_cb *gs_cb;
55
56 gs_cb = current->thread.gs_bc_cb;
57 if (!gs_cb) {
58 gs_cb = kzalloc(sizeof(*gs_cb), GFP_KERNEL);
59 if (!gs_cb)
60 return -ENOMEM;
61 current->thread.gs_bc_cb = gs_cb;
62 }
63 if (copy_from_user(gs_cb, u_gs_cb, sizeof(*gs_cb)))
64 return -EFAULT;
65 return 0;
66}
67
68static int gs_clear_bc_cb(void)
69{
70 struct gs_cb *gs_cb;
71
72 gs_cb = current->thread.gs_bc_cb;
73 current->thread.gs_bc_cb = NULL;
74 kfree(objp: gs_cb);
75 return 0;
76}
77
78void gs_load_bc_cb(struct pt_regs *regs)
79{
80 struct gs_cb *gs_cb;
81
82 preempt_disable();
83 clear_thread_flag(TIF_GUARDED_STORAGE);
84 gs_cb = current->thread.gs_bc_cb;
85 if (gs_cb) {
86 kfree(current->thread.gs_cb);
87 current->thread.gs_bc_cb = NULL;
88 local_ctl_set_bit(2, CR2_GUARDED_STORAGE_BIT);
89 load_gs_cb(gs_cb);
90 current->thread.gs_cb = gs_cb;
91 }
92 preempt_enable();
93}
94
95static int gs_broadcast(void)
96{
97 struct task_struct *sibling;
98
99 read_lock(&tasklist_lock);
100 for_each_thread(current, sibling) {
101 if (!sibling->thread.gs_bc_cb)
102 continue;
103 if (test_and_set_tsk_thread_flag(tsk: sibling, flag: TIF_GUARDED_STORAGE))
104 kick_process(tsk: sibling);
105 }
106 read_unlock(&tasklist_lock);
107 return 0;
108}
109
110SYSCALL_DEFINE2(s390_guarded_storage, int, command,
111 struct gs_cb __user *, gs_cb)
112{
113 if (!cpu_has_gs())
114 return -EOPNOTSUPP;
115 switch (command) {
116 case GS_ENABLE:
117 return gs_enable();
118 case GS_DISABLE:
119 return gs_disable();
120 case GS_SET_BC_CB:
121 return gs_set_bc_cb(u_gs_cb: gs_cb);
122 case GS_CLEAR_BC_CB:
123 return gs_clear_bc_cb();
124 case GS_BROADCAST:
125 return gs_broadcast();
126 default:
127 return -EINVAL;
128 }
129}
130

source code of linux/arch/s390/kernel/guarded_storage.c