/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include "hab.h" #include "hab_qvm.h" static unsigned long long xvm_sche_tx_tv_buffer[2]; static void pipe_read_trace(struct hab_pipe *pipe, struct hab_pipe_endpoint *ep, int size, int ret) { struct hab_shared_buf *sh_buf = ep->rx_info.sh_buf; struct dbg_items *its = (struct dbg_items *)pipe->buf_a; struct dbg_item *it = &its->it[its->idx]; it->rd_cnt = sh_buf->rd_count; it->wr_cnt = sh_buf->wr_count; it->va = (void *)&sh_buf->data[ep->rx_info.index]; it->index = ep->rx_info.index; it->sz = size; it->ret = ret; its->idx++; if (its->idx >= DBG_ITEM_SIZE) its->idx = 0; } /* this is only used to read payload, never the head! */ int physical_channel_read(struct physical_channel *pchan, void *payload, size_t read_size) { struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data; if (dev) { int ret = hab_pipe_read(dev->pipe_ep, payload, read_size, 0); /* log */ pipe_read_trace(dev->pipe, dev->pipe_ep, read_size, ret); return ret; } else return 0; } #define HAB_HEAD_SIGNATURE 0xBEE1BEE1 int physical_channel_send(struct physical_channel *pchan, struct hab_header *header, void *payload) { size_t sizebytes = HAB_HEADER_GET_SIZE(*header); struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data; size_t total_size = sizeof(*header) + sizebytes; int irqs_disabled = irqs_disabled(); if (total_size > dev->pipe_ep->tx_info.sh_buf->size) return -EINVAL; /* too much data for ring */ hab_spin_lock(&dev->io_lock, irqs_disabled); if ((dev->pipe_ep->tx_info.sh_buf->size - (dev->pipe_ep->tx_info.wr_count - dev->pipe_ep->tx_info.sh_buf->rd_count)) < total_size) { hab_spin_unlock(&dev->io_lock, irqs_disabled); return -EAGAIN; /* not enough free space */ } header->sequence = pchan->sequence_tx + 1; header->signature = HAB_HEAD_SIGNATURE; if (hab_pipe_write(dev->pipe_ep, (unsigned char *)header, sizeof(*header)) != sizeof(*header)) { hab_spin_unlock(&dev->io_lock, irqs_disabled); pr_err("***incompleted pchan send id-type-size %x session %d seq# %d\n", header->id_type_size, header->session_id, header->sequence); return -EIO; } if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_PROFILE) { struct timeval tv; struct habmm_xing_vm_stat *pstat = (struct habmm_xing_vm_stat *)payload; if (pstat) { do_gettimeofday(&tv); pstat->tx_sec = tv.tv_sec; pstat->tx_usec = tv.tv_usec; } else { hab_spin_unlock(&dev->io_lock, irqs_disabled); pr_err("***incompleted pchan send prof id-type-size %x session %d seq# %d\n", header->id_type_size, header->session_id, header->sequence); return -EINVAL; } } else if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_SCHE_RESULT_REQ) { ((unsigned long long *)payload)[0] = xvm_sche_tx_tv_buffer[0]; } else if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_SCHE_RESULT_RSP) { ((unsigned long long *)payload)[2] = xvm_sche_tx_tv_buffer[1]; } if (sizebytes) { if (hab_pipe_write(dev->pipe_ep, (unsigned char *)payload, sizebytes) != sizebytes) { hab_spin_unlock(&dev->io_lock, irqs_disabled); pr_err("***incompleted pchan send id-type-size %x session %d seq# %d\n", header->id_type_size, header->session_id, header->sequence); return -EIO; } } hab_pipe_write_commit(dev->pipe_ep); hab_spin_unlock(&dev->io_lock, irqs_disabled); if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_SCHE_MSG) xvm_sche_tx_tv_buffer[0] = msm_timer_get_sclk_ticks(); else if (HAB_HEADER_GET_TYPE(*header) == HAB_PAYLOAD_TYPE_SCHE_MSG_ACK) xvm_sche_tx_tv_buffer[1] = msm_timer_get_sclk_ticks(); habhyp_notify(dev); ++pchan->sequence_tx; return 0; } void physical_channel_rx_dispatch(unsigned long data) { struct hab_header header; struct physical_channel *pchan = (struct physical_channel *)data; struct qvm_channel *dev = (struct qvm_channel *)pchan->hyp_data; int irqs_disabled = irqs_disabled(); int i; hab_spin_lock(&pchan->rxbuf_lock, irqs_disabled); i = 0; while (1) { uint32_t rd, wr, idx; int ret; ret = hab_pipe_read(dev->pipe_ep, (unsigned char *)&header, sizeof(header), 1); /* clear head after read */ /* debug */ pipe_read_trace(dev->pipe, dev->pipe_ep, sizeof(header), ret); if (ret != sizeof(header)) break; /* no data available */ hab_pipe_rxinfo(dev->pipe_ep, &rd, &wr, &idx); if (header.signature != HAB_HEAD_SIGNATURE) { pr_err("!!!!! HAB signature mismatch expect %X received %X, id_type_size %X session %X sequence %X i %d\n", HAB_HEAD_SIGNATURE, header.signature, header.id_type_size, header.session_id, header.sequence, i); pr_err("!!!!! rxinfo rd %d wr %d index %X\n", rd, wr, idx); memcpy(dev->side_buf, (void *)&dev->pipe_ep->rx_info.sh_buf->data[0], dev->pipe_ep->rx_info.sh_buf->size); hab_spin_unlock(&pchan->rxbuf_lock, irqs_disabled); /* cannot run in elevated context */ dump_hab_wq(dev); hab_spin_lock(&pchan->rxbuf_lock, irqs_disabled); } pchan->sequence_rx = header.sequence; hab_msg_recv(pchan, &header); i++; } hab_spin_unlock(&pchan->rxbuf_lock, irqs_disabled); }