Merge branch 'upstream-acpi-fixes' into WIP.x86/pti.base
[linux-2.6-microblaze.git] / arch / s390 / kernel / guarded_storage.c
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/kernel.h>
8 #include <linux/syscalls.h>
9 #include <linux/signal.h>
10 #include <linux/mm.h>
11 #include <linux/slab.h>
12 #include <asm/guarded_storage.h>
13 #include "entry.h"
14
15 void exit_thread_gs(void)
16 {
17         kfree(current->thread.gs_cb);
18         kfree(current->thread.gs_bc_cb);
19         current->thread.gs_cb = current->thread.gs_bc_cb = NULL;
20 }
21
22 static 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                 __ctl_set_bit(2, 4);
33                 load_gs_cb(gs_cb);
34                 current->thread.gs_cb = gs_cb;
35                 preempt_enable();
36         }
37         return 0;
38 }
39
40 static 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                 __ctl_clear_bit(2, 4);
47                 preempt_enable();
48         }
49         return 0;
50 }
51
52 static 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
68 static 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(gs_cb);
75         return 0;
76 }
77
78 void 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                 __ctl_set_bit(2, 4);
89                 load_gs_cb(gs_cb);
90                 current->thread.gs_cb = gs_cb;
91         }
92         preempt_enable();
93 }
94
95 static 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(sibling, TIF_GUARDED_STORAGE))
104                         kick_process(sibling);
105         }
106         read_unlock(&tasklist_lock);
107         return 0;
108 }
109
110 SYSCALL_DEFINE2(s390_guarded_storage, int, command,
111                 struct gs_cb __user *, gs_cb)
112 {
113         if (!MACHINE_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(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 }