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