1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2916cda1aSMartin Schwidefsky /*
3916cda1aSMartin Schwidefsky * Copyright IBM Corp. 2016
4916cda1aSMartin Schwidefsky * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
5916cda1aSMartin Schwidefsky */
6916cda1aSMartin Schwidefsky
7916cda1aSMartin Schwidefsky #include <linux/kernel.h>
8916cda1aSMartin Schwidefsky #include <linux/syscalls.h>
9916cda1aSMartin Schwidefsky #include <linux/signal.h>
10916cda1aSMartin Schwidefsky #include <linux/mm.h>
11916cda1aSMartin Schwidefsky #include <linux/slab.h>
12916cda1aSMartin Schwidefsky #include <asm/guarded_storage.h>
13916cda1aSMartin Schwidefsky #include "entry.h"
14916cda1aSMartin Schwidefsky
guarded_storage_release(struct task_struct * tsk)157b83c629SHeiko Carstens void guarded_storage_release(struct task_struct *tsk)
16916cda1aSMartin Schwidefsky {
177b83c629SHeiko Carstens kfree(tsk->thread.gs_cb);
187b83c629SHeiko Carstens kfree(tsk->thread.gs_bc_cb);
19916cda1aSMartin Schwidefsky }
20916cda1aSMartin Schwidefsky
gs_enable(void)21916cda1aSMartin Schwidefsky static int gs_enable(void)
22916cda1aSMartin Schwidefsky {
23916cda1aSMartin Schwidefsky struct gs_cb *gs_cb;
24916cda1aSMartin Schwidefsky
25916cda1aSMartin Schwidefsky if (!current->thread.gs_cb) {
26916cda1aSMartin Schwidefsky gs_cb = kzalloc(sizeof(*gs_cb), GFP_KERNEL);
27916cda1aSMartin Schwidefsky if (!gs_cb)
28916cda1aSMartin Schwidefsky return -ENOMEM;
29916cda1aSMartin Schwidefsky gs_cb->gsd = 25;
30916cda1aSMartin Schwidefsky preempt_disable();
31916cda1aSMartin Schwidefsky __ctl_set_bit(2, 4);
32916cda1aSMartin Schwidefsky load_gs_cb(gs_cb);
33916cda1aSMartin Schwidefsky current->thread.gs_cb = gs_cb;
34916cda1aSMartin Schwidefsky preempt_enable();
35916cda1aSMartin Schwidefsky }
36916cda1aSMartin Schwidefsky return 0;
37916cda1aSMartin Schwidefsky }
38916cda1aSMartin Schwidefsky
gs_disable(void)39916cda1aSMartin Schwidefsky static int gs_disable(void)
40916cda1aSMartin Schwidefsky {
41916cda1aSMartin Schwidefsky if (current->thread.gs_cb) {
42916cda1aSMartin Schwidefsky preempt_disable();
43916cda1aSMartin Schwidefsky kfree(current->thread.gs_cb);
44916cda1aSMartin Schwidefsky current->thread.gs_cb = NULL;
45916cda1aSMartin Schwidefsky __ctl_clear_bit(2, 4);
46916cda1aSMartin Schwidefsky preempt_enable();
47916cda1aSMartin Schwidefsky }
48916cda1aSMartin Schwidefsky return 0;
49916cda1aSMartin Schwidefsky }
50916cda1aSMartin Schwidefsky
gs_set_bc_cb(struct gs_cb __user * u_gs_cb)51916cda1aSMartin Schwidefsky static int gs_set_bc_cb(struct gs_cb __user *u_gs_cb)
52916cda1aSMartin Schwidefsky {
53916cda1aSMartin Schwidefsky struct gs_cb *gs_cb;
54916cda1aSMartin Schwidefsky
55916cda1aSMartin Schwidefsky gs_cb = current->thread.gs_bc_cb;
56916cda1aSMartin Schwidefsky if (!gs_cb) {
57916cda1aSMartin Schwidefsky gs_cb = kzalloc(sizeof(*gs_cb), GFP_KERNEL);
58916cda1aSMartin Schwidefsky if (!gs_cb)
59916cda1aSMartin Schwidefsky return -ENOMEM;
60916cda1aSMartin Schwidefsky current->thread.gs_bc_cb = gs_cb;
61916cda1aSMartin Schwidefsky }
62916cda1aSMartin Schwidefsky if (copy_from_user(gs_cb, u_gs_cb, sizeof(*gs_cb)))
63916cda1aSMartin Schwidefsky return -EFAULT;
64916cda1aSMartin Schwidefsky return 0;
65916cda1aSMartin Schwidefsky }
66916cda1aSMartin Schwidefsky
gs_clear_bc_cb(void)67916cda1aSMartin Schwidefsky static int gs_clear_bc_cb(void)
68916cda1aSMartin Schwidefsky {
69916cda1aSMartin Schwidefsky struct gs_cb *gs_cb;
70916cda1aSMartin Schwidefsky
71916cda1aSMartin Schwidefsky gs_cb = current->thread.gs_bc_cb;
72916cda1aSMartin Schwidefsky current->thread.gs_bc_cb = NULL;
73916cda1aSMartin Schwidefsky kfree(gs_cb);
74916cda1aSMartin Schwidefsky return 0;
75916cda1aSMartin Schwidefsky }
76916cda1aSMartin Schwidefsky
gs_load_bc_cb(struct pt_regs * regs)77916cda1aSMartin Schwidefsky void gs_load_bc_cb(struct pt_regs *regs)
78916cda1aSMartin Schwidefsky {
79916cda1aSMartin Schwidefsky struct gs_cb *gs_cb;
80916cda1aSMartin Schwidefsky
81916cda1aSMartin Schwidefsky preempt_disable();
82916cda1aSMartin Schwidefsky clear_thread_flag(TIF_GUARDED_STORAGE);
83916cda1aSMartin Schwidefsky gs_cb = current->thread.gs_bc_cb;
84916cda1aSMartin Schwidefsky if (gs_cb) {
85916cda1aSMartin Schwidefsky kfree(current->thread.gs_cb);
86916cda1aSMartin Schwidefsky current->thread.gs_bc_cb = NULL;
87916cda1aSMartin Schwidefsky __ctl_set_bit(2, 4);
88916cda1aSMartin Schwidefsky load_gs_cb(gs_cb);
89916cda1aSMartin Schwidefsky current->thread.gs_cb = gs_cb;
90916cda1aSMartin Schwidefsky }
91916cda1aSMartin Schwidefsky preempt_enable();
92916cda1aSMartin Schwidefsky }
93916cda1aSMartin Schwidefsky
gs_broadcast(void)94916cda1aSMartin Schwidefsky static int gs_broadcast(void)
95916cda1aSMartin Schwidefsky {
96916cda1aSMartin Schwidefsky struct task_struct *sibling;
97916cda1aSMartin Schwidefsky
98916cda1aSMartin Schwidefsky read_lock(&tasklist_lock);
99916cda1aSMartin Schwidefsky for_each_thread(current, sibling) {
100916cda1aSMartin Schwidefsky if (!sibling->thread.gs_bc_cb)
101916cda1aSMartin Schwidefsky continue;
102916cda1aSMartin Schwidefsky if (test_and_set_tsk_thread_flag(sibling, TIF_GUARDED_STORAGE))
103916cda1aSMartin Schwidefsky kick_process(sibling);
104916cda1aSMartin Schwidefsky }
105916cda1aSMartin Schwidefsky read_unlock(&tasklist_lock);
106916cda1aSMartin Schwidefsky return 0;
107916cda1aSMartin Schwidefsky }
108916cda1aSMartin Schwidefsky
SYSCALL_DEFINE2(s390_guarded_storage,int,command,struct gs_cb __user *,gs_cb)109916cda1aSMartin Schwidefsky SYSCALL_DEFINE2(s390_guarded_storage, int, command,
110916cda1aSMartin Schwidefsky struct gs_cb __user *, gs_cb)
111916cda1aSMartin Schwidefsky {
112916cda1aSMartin Schwidefsky if (!MACHINE_HAS_GS)
113916cda1aSMartin Schwidefsky return -EOPNOTSUPP;
114916cda1aSMartin Schwidefsky switch (command) {
115916cda1aSMartin Schwidefsky case GS_ENABLE:
116916cda1aSMartin Schwidefsky return gs_enable();
117916cda1aSMartin Schwidefsky case GS_DISABLE:
118916cda1aSMartin Schwidefsky return gs_disable();
119916cda1aSMartin Schwidefsky case GS_SET_BC_CB:
120916cda1aSMartin Schwidefsky return gs_set_bc_cb(gs_cb);
121916cda1aSMartin Schwidefsky case GS_CLEAR_BC_CB:
122916cda1aSMartin Schwidefsky return gs_clear_bc_cb();
123916cda1aSMartin Schwidefsky case GS_BROADCAST:
124916cda1aSMartin Schwidefsky return gs_broadcast();
125916cda1aSMartin Schwidefsky default:
126916cda1aSMartin Schwidefsky return -EINVAL;
127916cda1aSMartin Schwidefsky }
128916cda1aSMartin Schwidefsky }
129