cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

opal-async.c (7370B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * PowerNV OPAL asynchronous completion interfaces
      4 *
      5 * Copyright 2013-2017 IBM Corp.
      6 */
      7
      8#undef DEBUG
      9
     10#include <linux/kernel.h>
     11#include <linux/init.h>
     12#include <linux/slab.h>
     13#include <linux/sched.h>
     14#include <linux/semaphore.h>
     15#include <linux/spinlock.h>
     16#include <linux/wait.h>
     17#include <linux/gfp.h>
     18#include <linux/of.h>
     19#include <asm/machdep.h>
     20#include <asm/opal.h>
     21
     22enum opal_async_token_state {
     23	ASYNC_TOKEN_UNALLOCATED = 0,
     24	ASYNC_TOKEN_ALLOCATED,
     25	ASYNC_TOKEN_DISPATCHED,
     26	ASYNC_TOKEN_ABANDONED,
     27	ASYNC_TOKEN_COMPLETED
     28};
     29
     30struct opal_async_token {
     31	enum opal_async_token_state state;
     32	struct opal_msg response;
     33};
     34
     35static DECLARE_WAIT_QUEUE_HEAD(opal_async_wait);
     36static DEFINE_SPINLOCK(opal_async_comp_lock);
     37static struct semaphore opal_async_sem;
     38static unsigned int opal_max_async_tokens;
     39static struct opal_async_token *opal_async_tokens;
     40
     41static int __opal_async_get_token(void)
     42{
     43	unsigned long flags;
     44	int i, token = -EBUSY;
     45
     46	spin_lock_irqsave(&opal_async_comp_lock, flags);
     47
     48	for (i = 0; i < opal_max_async_tokens; i++) {
     49		if (opal_async_tokens[i].state == ASYNC_TOKEN_UNALLOCATED) {
     50			opal_async_tokens[i].state = ASYNC_TOKEN_ALLOCATED;
     51			token = i;
     52			break;
     53		}
     54	}
     55
     56	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
     57	return token;
     58}
     59
     60/*
     61 * Note: If the returned token is used in an opal call and opal returns
     62 * OPAL_ASYNC_COMPLETION you MUST call one of opal_async_wait_response() or
     63 * opal_async_wait_response_interruptible() at least once before calling another
     64 * opal_async_* function
     65 */
     66int opal_async_get_token_interruptible(void)
     67{
     68	int token;
     69
     70	/* Wait until a token is available */
     71	if (down_interruptible(&opal_async_sem))
     72		return -ERESTARTSYS;
     73
     74	token = __opal_async_get_token();
     75	if (token < 0)
     76		up(&opal_async_sem);
     77
     78	return token;
     79}
     80EXPORT_SYMBOL_GPL(opal_async_get_token_interruptible);
     81
     82static int __opal_async_release_token(int token)
     83{
     84	unsigned long flags;
     85	int rc;
     86
     87	if (token < 0 || token >= opal_max_async_tokens) {
     88		pr_err("%s: Passed token is out of range, token %d\n",
     89				__func__, token);
     90		return -EINVAL;
     91	}
     92
     93	spin_lock_irqsave(&opal_async_comp_lock, flags);
     94	switch (opal_async_tokens[token].state) {
     95	case ASYNC_TOKEN_COMPLETED:
     96	case ASYNC_TOKEN_ALLOCATED:
     97		opal_async_tokens[token].state = ASYNC_TOKEN_UNALLOCATED;
     98		rc = 0;
     99		break;
    100	/*
    101	 * DISPATCHED and ABANDONED tokens must wait for OPAL to respond.
    102	 * Mark a DISPATCHED token as ABANDONED so that the response handling
    103	 * code knows no one cares and that it can free it then.
    104	 */
    105	case ASYNC_TOKEN_DISPATCHED:
    106		opal_async_tokens[token].state = ASYNC_TOKEN_ABANDONED;
    107		fallthrough;
    108	default:
    109		rc = 1;
    110	}
    111	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
    112
    113	return rc;
    114}
    115
    116int opal_async_release_token(int token)
    117{
    118	int ret;
    119
    120	ret = __opal_async_release_token(token);
    121	if (!ret)
    122		up(&opal_async_sem);
    123
    124	return ret;
    125}
    126EXPORT_SYMBOL_GPL(opal_async_release_token);
    127
    128int opal_async_wait_response(uint64_t token, struct opal_msg *msg)
    129{
    130	if (token >= opal_max_async_tokens) {
    131		pr_err("%s: Invalid token passed\n", __func__);
    132		return -EINVAL;
    133	}
    134
    135	if (!msg) {
    136		pr_err("%s: Invalid message pointer passed\n", __func__);
    137		return -EINVAL;
    138	}
    139
    140	/*
    141	 * There is no need to mark the token as dispatched, wait_event()
    142	 * will block until the token completes.
    143	 *
    144	 * Wakeup the poller before we wait for events to speed things
    145	 * up on platforms or simulators where the interrupts aren't
    146	 * functional.
    147	 */
    148	opal_wake_poller();
    149	wait_event(opal_async_wait, opal_async_tokens[token].state
    150			== ASYNC_TOKEN_COMPLETED);
    151	memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));
    152
    153	return 0;
    154}
    155EXPORT_SYMBOL_GPL(opal_async_wait_response);
    156
    157int opal_async_wait_response_interruptible(uint64_t token, struct opal_msg *msg)
    158{
    159	unsigned long flags;
    160	int ret;
    161
    162	if (token >= opal_max_async_tokens) {
    163		pr_err("%s: Invalid token passed\n", __func__);
    164		return -EINVAL;
    165	}
    166
    167	if (!msg) {
    168		pr_err("%s: Invalid message pointer passed\n", __func__);
    169		return -EINVAL;
    170	}
    171
    172	/*
    173	 * The first time this gets called we mark the token as DISPATCHED
    174	 * so that if wait_event_interruptible() returns not zero and the
    175	 * caller frees the token, we know not to actually free the token
    176	 * until the response comes.
    177	 *
    178	 * Only change if the token is ALLOCATED - it may have been
    179	 * completed even before the caller gets around to calling this
    180	 * the first time.
    181	 *
    182	 * There is also a dirty great comment at the token allocation
    183	 * function that if the opal call returns OPAL_ASYNC_COMPLETION to
    184	 * the caller then the caller *must* call this or the not
    185	 * interruptible version before doing anything else with the
    186	 * token.
    187	 */
    188	if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED) {
    189		spin_lock_irqsave(&opal_async_comp_lock, flags);
    190		if (opal_async_tokens[token].state == ASYNC_TOKEN_ALLOCATED)
    191			opal_async_tokens[token].state = ASYNC_TOKEN_DISPATCHED;
    192		spin_unlock_irqrestore(&opal_async_comp_lock, flags);
    193	}
    194
    195	/*
    196	 * Wakeup the poller before we wait for events to speed things
    197	 * up on platforms or simulators where the interrupts aren't
    198	 * functional.
    199	 */
    200	opal_wake_poller();
    201	ret = wait_event_interruptible(opal_async_wait,
    202			opal_async_tokens[token].state ==
    203			ASYNC_TOKEN_COMPLETED);
    204	if (!ret)
    205		memcpy(msg, &opal_async_tokens[token].response, sizeof(*msg));
    206
    207	return ret;
    208}
    209EXPORT_SYMBOL_GPL(opal_async_wait_response_interruptible);
    210
    211/* Called from interrupt context */
    212static int opal_async_comp_event(struct notifier_block *nb,
    213		unsigned long msg_type, void *msg)
    214{
    215	struct opal_msg *comp_msg = msg;
    216	enum opal_async_token_state state;
    217	unsigned long flags;
    218	uint64_t token;
    219
    220	if (msg_type != OPAL_MSG_ASYNC_COMP)
    221		return 0;
    222
    223	token = be64_to_cpu(comp_msg->params[0]);
    224	spin_lock_irqsave(&opal_async_comp_lock, flags);
    225	state = opal_async_tokens[token].state;
    226	opal_async_tokens[token].state = ASYNC_TOKEN_COMPLETED;
    227	spin_unlock_irqrestore(&opal_async_comp_lock, flags);
    228
    229	if (state == ASYNC_TOKEN_ABANDONED) {
    230		/* Free the token, no one else will */
    231		opal_async_release_token(token);
    232		return 0;
    233	}
    234	memcpy(&opal_async_tokens[token].response, comp_msg, sizeof(*comp_msg));
    235	wake_up(&opal_async_wait);
    236
    237	return 0;
    238}
    239
    240static struct notifier_block opal_async_comp_nb = {
    241		.notifier_call	= opal_async_comp_event,
    242		.next		= NULL,
    243		.priority	= 0,
    244};
    245
    246int __init opal_async_comp_init(void)
    247{
    248	struct device_node *opal_node;
    249	const __be32 *async;
    250	int err;
    251
    252	opal_node = of_find_node_by_path("/ibm,opal");
    253	if (!opal_node) {
    254		pr_err("%s: Opal node not found\n", __func__);
    255		err = -ENOENT;
    256		goto out;
    257	}
    258
    259	async = of_get_property(opal_node, "opal-msg-async-num", NULL);
    260	if (!async) {
    261		pr_err("%s: %pOF has no opal-msg-async-num\n",
    262				__func__, opal_node);
    263		err = -ENOENT;
    264		goto out_opal_node;
    265	}
    266
    267	opal_max_async_tokens = be32_to_cpup(async);
    268	opal_async_tokens = kcalloc(opal_max_async_tokens,
    269			sizeof(*opal_async_tokens), GFP_KERNEL);
    270	if (!opal_async_tokens) {
    271		err = -ENOMEM;
    272		goto out_opal_node;
    273	}
    274
    275	err = opal_message_notifier_register(OPAL_MSG_ASYNC_COMP,
    276			&opal_async_comp_nb);
    277	if (err) {
    278		pr_err("%s: Can't register OPAL event notifier (%d)\n",
    279				__func__, err);
    280		kfree(opal_async_tokens);
    281		goto out_opal_node;
    282	}
    283
    284	sema_init(&opal_async_sem, opal_max_async_tokens);
    285
    286out_opal_node:
    287	of_node_put(opal_node);
    288out:
    289	return err;
    290}