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