mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-23 20:24:12 +08:00
8d5e98f8d6
Add local and system prefix to some functions to clarify they change control register contents on either the local CPU or the on all CPUs. This results in the following API: Two defines which load and save multiple control registers. The defines correlate with the following C prototypes: void __local_ctl_load(unsigned long *, unsigned int cr_low, unsigned int cr_high); void __local_ctl_store(unsigned long *, unsigned int cr_low, unsigned int cr_high); Two functions which locally set or clear one bit for a specified control register: void local_ctl_set_bit(unsigned int cr, unsigned int bit); void local_ctl_clear_bit(unsigned int cr, unsigned int bit); Two functions which set or clear one bit for a specified control register on all CPUs: void system_ctl_set_bit(unsigned int cr, unsigned int bit); void system_ctl_clear_bit(unsigend int cr, unsigned int bit); Reviewed-by: Alexander Gordeev <agordeev@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
129 lines
2.5 KiB
C
129 lines
2.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright IBM Corp. 2016
|
|
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/signal.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
#include <asm/guarded_storage.h>
|
|
#include "entry.h"
|
|
|
|
void guarded_storage_release(struct task_struct *tsk)
|
|
{
|
|
kfree(tsk->thread.gs_cb);
|
|
kfree(tsk->thread.gs_bc_cb);
|
|
}
|
|
|
|
static int gs_enable(void)
|
|
{
|
|
struct gs_cb *gs_cb;
|
|
|
|
if (!current->thread.gs_cb) {
|
|
gs_cb = kzalloc(sizeof(*gs_cb), GFP_KERNEL);
|
|
if (!gs_cb)
|
|
return -ENOMEM;
|
|
gs_cb->gsd = 25;
|
|
preempt_disable();
|
|
local_ctl_set_bit(2, 4);
|
|
load_gs_cb(gs_cb);
|
|
current->thread.gs_cb = gs_cb;
|
|
preempt_enable();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int gs_disable(void)
|
|
{
|
|
if (current->thread.gs_cb) {
|
|
preempt_disable();
|
|
kfree(current->thread.gs_cb);
|
|
current->thread.gs_cb = NULL;
|
|
local_ctl_clear_bit(2, 4);
|
|
preempt_enable();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int gs_set_bc_cb(struct gs_cb __user *u_gs_cb)
|
|
{
|
|
struct gs_cb *gs_cb;
|
|
|
|
gs_cb = current->thread.gs_bc_cb;
|
|
if (!gs_cb) {
|
|
gs_cb = kzalloc(sizeof(*gs_cb), GFP_KERNEL);
|
|
if (!gs_cb)
|
|
return -ENOMEM;
|
|
current->thread.gs_bc_cb = gs_cb;
|
|
}
|
|
if (copy_from_user(gs_cb, u_gs_cb, sizeof(*gs_cb)))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
|
|
static int gs_clear_bc_cb(void)
|
|
{
|
|
struct gs_cb *gs_cb;
|
|
|
|
gs_cb = current->thread.gs_bc_cb;
|
|
current->thread.gs_bc_cb = NULL;
|
|
kfree(gs_cb);
|
|
return 0;
|
|
}
|
|
|
|
void gs_load_bc_cb(struct pt_regs *regs)
|
|
{
|
|
struct gs_cb *gs_cb;
|
|
|
|
preempt_disable();
|
|
clear_thread_flag(TIF_GUARDED_STORAGE);
|
|
gs_cb = current->thread.gs_bc_cb;
|
|
if (gs_cb) {
|
|
kfree(current->thread.gs_cb);
|
|
current->thread.gs_bc_cb = NULL;
|
|
local_ctl_set_bit(2, 4);
|
|
load_gs_cb(gs_cb);
|
|
current->thread.gs_cb = gs_cb;
|
|
}
|
|
preempt_enable();
|
|
}
|
|
|
|
static int gs_broadcast(void)
|
|
{
|
|
struct task_struct *sibling;
|
|
|
|
read_lock(&tasklist_lock);
|
|
for_each_thread(current, sibling) {
|
|
if (!sibling->thread.gs_bc_cb)
|
|
continue;
|
|
if (test_and_set_tsk_thread_flag(sibling, TIF_GUARDED_STORAGE))
|
|
kick_process(sibling);
|
|
}
|
|
read_unlock(&tasklist_lock);
|
|
return 0;
|
|
}
|
|
|
|
SYSCALL_DEFINE2(s390_guarded_storage, int, command,
|
|
struct gs_cb __user *, gs_cb)
|
|
{
|
|
if (!MACHINE_HAS_GS)
|
|
return -EOPNOTSUPP;
|
|
switch (command) {
|
|
case GS_ENABLE:
|
|
return gs_enable();
|
|
case GS_DISABLE:
|
|
return gs_disable();
|
|
case GS_SET_BC_CB:
|
|
return gs_set_bc_cb(gs_cb);
|
|
case GS_CLEAR_BC_CB:
|
|
return gs_clear_bc_cb();
|
|
case GS_BROADCAST:
|
|
return gs_broadcast();
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|