xref: /openbmc/linux/arch/s390/kernel/guarded_storage.c (revision b24413180f5600bcb3bb70fbed5cf186b60864bd)
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 }
130