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

iosm_ipc_protocol.c (7993B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2020-21 Intel Corporation.
      4 */
      5
      6#include "iosm_ipc_imem.h"
      7#include "iosm_ipc_protocol.h"
      8#include "iosm_ipc_protocol_ops.h"
      9#include "iosm_ipc_pm.h"
     10#include "iosm_ipc_task_queue.h"
     11
     12int ipc_protocol_tq_msg_send(struct iosm_protocol *ipc_protocol,
     13			     enum ipc_msg_prep_type msg_type,
     14			     union ipc_msg_prep_args *prep_args,
     15			     struct ipc_rsp *response)
     16{
     17	int index = ipc_protocol_msg_prep(ipc_protocol->imem, msg_type,
     18					  prep_args);
     19
     20	/* Store reference towards caller specified response in response ring
     21	 * and signal CP
     22	 */
     23	if (index >= 0 && index < IPC_MEM_MSG_ENTRIES) {
     24		ipc_protocol->rsp_ring[index] = response;
     25		ipc_protocol_msg_hp_update(ipc_protocol->imem);
     26	}
     27
     28	return index;
     29}
     30
     31/* Callback for message send */
     32static int ipc_protocol_tq_msg_send_cb(struct iosm_imem *ipc_imem, int arg,
     33				       void *msg, size_t size)
     34{
     35	struct ipc_call_msg_send_args *send_args = msg;
     36	struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol;
     37
     38	return ipc_protocol_tq_msg_send(ipc_protocol, send_args->msg_type,
     39					send_args->prep_args,
     40					send_args->response);
     41}
     42
     43/* Remove reference to a response. This is typically used when a requestor timed
     44 * out and is no longer interested in the response.
     45 */
     46static int ipc_protocol_tq_msg_remove(struct iosm_imem *ipc_imem, int arg,
     47				      void *msg, size_t size)
     48{
     49	struct iosm_protocol *ipc_protocol = ipc_imem->ipc_protocol;
     50
     51	ipc_protocol->rsp_ring[arg] = NULL;
     52	return 0;
     53}
     54
     55int ipc_protocol_msg_send(struct iosm_protocol *ipc_protocol,
     56			  enum ipc_msg_prep_type prep,
     57			  union ipc_msg_prep_args *prep_args)
     58{
     59	struct ipc_call_msg_send_args send_args;
     60	unsigned int exec_timeout;
     61	struct ipc_rsp response;
     62	int index;
     63
     64	exec_timeout = (ipc_protocol_get_ap_exec_stage(ipc_protocol) ==
     65					IPC_MEM_EXEC_STAGE_RUN ?
     66				IPC_MSG_COMPLETE_RUN_DEFAULT_TIMEOUT :
     67				IPC_MSG_COMPLETE_BOOT_DEFAULT_TIMEOUT);
     68
     69	/* Trap if called from non-preemptible context */
     70	might_sleep();
     71
     72	response.status = IPC_MEM_MSG_CS_INVALID;
     73	init_completion(&response.completion);
     74
     75	send_args.msg_type = prep;
     76	send_args.prep_args = prep_args;
     77	send_args.response = &response;
     78
     79	/* Allocate and prepare message to be sent in tasklet context.
     80	 * A positive index returned form tasklet_call references the message
     81	 * in case it needs to be cancelled when there is a timeout.
     82	 */
     83	index = ipc_task_queue_send_task(ipc_protocol->imem,
     84					 ipc_protocol_tq_msg_send_cb, 0,
     85					 &send_args, 0, true);
     86
     87	if (index < 0) {
     88		dev_err(ipc_protocol->dev, "msg %d failed", prep);
     89		return index;
     90	}
     91
     92	/* Wait for the device to respond to the message */
     93	switch (wait_for_completion_timeout(&response.completion,
     94					    msecs_to_jiffies(exec_timeout))) {
     95	case 0:
     96		/* Timeout, there was no response from the device.
     97		 * Remove the reference to the local response completion
     98		 * object as we are no longer interested in the response.
     99		 */
    100		ipc_task_queue_send_task(ipc_protocol->imem,
    101					 ipc_protocol_tq_msg_remove, index,
    102					 NULL, 0, true);
    103		dev_err(ipc_protocol->dev, "msg timeout");
    104		ipc_uevent_send(ipc_protocol->pcie->dev, UEVENT_MDM_TIMEOUT);
    105		break;
    106	default:
    107		/* We got a response in time; check completion status: */
    108		if (response.status != IPC_MEM_MSG_CS_SUCCESS) {
    109			dev_err(ipc_protocol->dev,
    110				"msg completion status error %d",
    111				response.status);
    112			return -EIO;
    113		}
    114	}
    115
    116	return 0;
    117}
    118
    119static int ipc_protocol_msg_send_host_sleep(struct iosm_protocol *ipc_protocol,
    120					    u32 state)
    121{
    122	union ipc_msg_prep_args prep_args = {
    123		.sleep.target = 0,
    124		.sleep.state = state,
    125	};
    126
    127	return ipc_protocol_msg_send(ipc_protocol, IPC_MSG_PREP_SLEEP,
    128				     &prep_args);
    129}
    130
    131void ipc_protocol_doorbell_trigger(struct iosm_protocol *ipc_protocol,
    132				   u32 identifier)
    133{
    134	ipc_pm_signal_hpda_doorbell(&ipc_protocol->pm, identifier, true);
    135}
    136
    137bool ipc_protocol_pm_dev_sleep_handle(struct iosm_protocol *ipc_protocol)
    138{
    139	u32 ipc_status = ipc_protocol_get_ipc_status(ipc_protocol);
    140	u32 requested;
    141
    142	if (ipc_status != IPC_MEM_DEVICE_IPC_RUNNING) {
    143		dev_err(ipc_protocol->dev,
    144			"irq ignored, CP IPC state is %d, should be RUNNING",
    145			ipc_status);
    146
    147		/* Stop further processing. */
    148		return false;
    149	}
    150
    151	/* Get a copy of the requested PM state by the device and the local
    152	 * device PM state.
    153	 */
    154	requested = ipc_protocol_pm_dev_get_sleep_notification(ipc_protocol);
    155
    156	return ipc_pm_dev_slp_notification(&ipc_protocol->pm, requested);
    157}
    158
    159static int ipc_protocol_tq_wakeup_dev_slp(struct iosm_imem *ipc_imem, int arg,
    160					  void *msg, size_t size)
    161{
    162	struct iosm_pm *ipc_pm = &ipc_imem->ipc_protocol->pm;
    163
    164	/* Wakeup from device sleep if it is not ACTIVE */
    165	ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_HS, true);
    166
    167	ipc_pm_trigger(ipc_pm, IPC_PM_UNIT_HS, false);
    168
    169	return 0;
    170}
    171
    172void ipc_protocol_s2idle_sleep(struct iosm_protocol *ipc_protocol, bool sleep)
    173{
    174	ipc_pm_set_s2idle_sleep(&ipc_protocol->pm, sleep);
    175}
    176
    177bool ipc_protocol_suspend(struct iosm_protocol *ipc_protocol)
    178{
    179	if (!ipc_pm_prepare_host_sleep(&ipc_protocol->pm))
    180		goto err;
    181
    182	ipc_task_queue_send_task(ipc_protocol->imem,
    183				 ipc_protocol_tq_wakeup_dev_slp, 0, NULL, 0,
    184				 true);
    185
    186	if (!ipc_pm_wait_for_device_active(&ipc_protocol->pm)) {
    187		ipc_uevent_send(ipc_protocol->pcie->dev, UEVENT_MDM_TIMEOUT);
    188		goto err;
    189	}
    190
    191	/* Send the sleep message for sync sys calls. */
    192	dev_dbg(ipc_protocol->dev, "send TARGET_HOST, ENTER_SLEEP");
    193	if (ipc_protocol_msg_send_host_sleep(ipc_protocol,
    194					     IPC_HOST_SLEEP_ENTER_SLEEP)) {
    195		/* Sending ENTER_SLEEP message failed, we are still active */
    196		ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_ACTIVE;
    197		goto err;
    198	}
    199
    200	ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_SLEEP;
    201	return true;
    202err:
    203	return false;
    204}
    205
    206bool ipc_protocol_resume(struct iosm_protocol *ipc_protocol)
    207{
    208	if (!ipc_pm_prepare_host_active(&ipc_protocol->pm))
    209		return false;
    210
    211	dev_dbg(ipc_protocol->dev, "send TARGET_HOST, EXIT_SLEEP");
    212	if (ipc_protocol_msg_send_host_sleep(ipc_protocol,
    213					     IPC_HOST_SLEEP_EXIT_SLEEP)) {
    214		ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_SLEEP;
    215		return false;
    216	}
    217
    218	ipc_protocol->pm.host_pm_state = IPC_MEM_HOST_PM_ACTIVE;
    219
    220	return true;
    221}
    222
    223struct iosm_protocol *ipc_protocol_init(struct iosm_imem *ipc_imem)
    224{
    225	struct iosm_protocol *ipc_protocol =
    226		kzalloc(sizeof(*ipc_protocol), GFP_KERNEL);
    227	struct ipc_protocol_context_info *p_ci;
    228	u64 addr;
    229
    230	if (!ipc_protocol)
    231		return NULL;
    232
    233	ipc_protocol->dev = ipc_imem->dev;
    234	ipc_protocol->pcie = ipc_imem->pcie;
    235	ipc_protocol->imem = ipc_imem;
    236	ipc_protocol->p_ap_shm = NULL;
    237	ipc_protocol->phy_ap_shm = 0;
    238
    239	ipc_protocol->old_msg_tail = 0;
    240
    241	ipc_protocol->p_ap_shm =
    242		dma_alloc_coherent(&ipc_protocol->pcie->pci->dev,
    243				   sizeof(*ipc_protocol->p_ap_shm),
    244				   &ipc_protocol->phy_ap_shm, GFP_KERNEL);
    245
    246	if (!ipc_protocol->p_ap_shm) {
    247		dev_err(ipc_protocol->dev, "pci shm alloc error");
    248		kfree(ipc_protocol);
    249		return NULL;
    250	}
    251
    252	/* Prepare the context info for CP. */
    253	addr = ipc_protocol->phy_ap_shm;
    254	p_ci = &ipc_protocol->p_ap_shm->ci;
    255	p_ci->device_info_addr =
    256		addr + offsetof(struct ipc_protocol_ap_shm, device_info);
    257	p_ci->head_array =
    258		addr + offsetof(struct ipc_protocol_ap_shm, head_array);
    259	p_ci->tail_array =
    260		addr + offsetof(struct ipc_protocol_ap_shm, tail_array);
    261	p_ci->msg_head = addr + offsetof(struct ipc_protocol_ap_shm, msg_head);
    262	p_ci->msg_tail = addr + offsetof(struct ipc_protocol_ap_shm, msg_tail);
    263	p_ci->msg_ring_addr =
    264		addr + offsetof(struct ipc_protocol_ap_shm, msg_ring);
    265	p_ci->msg_ring_entries = cpu_to_le16(IPC_MEM_MSG_ENTRIES);
    266	p_ci->msg_irq_vector = IPC_MSG_IRQ_VECTOR;
    267	p_ci->device_info_irq_vector = IPC_DEVICE_IRQ_VECTOR;
    268
    269	ipc_mmio_set_contex_info_addr(ipc_imem->mmio, addr);
    270
    271	ipc_pm_init(ipc_protocol);
    272
    273	return ipc_protocol;
    274}
    275
    276void ipc_protocol_deinit(struct iosm_protocol *proto)
    277{
    278	dma_free_coherent(&proto->pcie->pci->dev, sizeof(*proto->p_ap_shm),
    279			  proto->p_ap_shm, proto->phy_ap_shm);
    280
    281	ipc_pm_deinit(proto);
    282	kfree(proto);
    283}