Files

202 lines
5.8 KiB
C

/* 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_pipe.h"
size_t hab_pipe_calc_required_bytes(uint32_t shared_buf_size)
{
return sizeof(struct hab_pipe)
+ (2 * (sizeof(struct hab_shared_buf) + shared_buf_size));
}
struct hab_pipe_endpoint *hab_pipe_init(struct hab_pipe *pipe,
uint32_t shared_buf_size, int top)
{
struct hab_pipe_endpoint *ep = NULL;
struct hab_shared_buf *buf_a;
struct hab_shared_buf *buf_b;
/* debug only */
struct dbg_items *its = kzalloc(sizeof(struct dbg_items), GFP_KERNEL);
if (!pipe)
return NULL;
buf_a = (struct hab_shared_buf *) pipe->buf_base;
buf_b = (struct hab_shared_buf *) (pipe->buf_base
+ sizeof(struct hab_shared_buf) + shared_buf_size);
if (top) {
ep = &pipe->top;
memset(ep, 0, sizeof(*ep));
ep->tx_info.sh_buf = buf_a;
ep->rx_info.sh_buf = buf_b;
} else {
ep = &pipe->bottom;
memset(ep, 0, sizeof(*ep));
ep->tx_info.sh_buf = buf_b;
ep->rx_info.sh_buf = buf_a;
memset(ep->tx_info.sh_buf, 0, sizeof(struct hab_shared_buf));
memset(ep->rx_info.sh_buf, 0, sizeof(struct hab_shared_buf));
ep->tx_info.sh_buf->size = shared_buf_size;
ep->rx_info.sh_buf->size = shared_buf_size;
pipe->buf_a = buf_a;
pipe->buf_b = buf_b;
pipe->total_size =
hab_pipe_calc_required_bytes(shared_buf_size);
}
pipe->buf_a = (struct hab_shared_buf *)its;
return ep;
}
uint32_t hab_pipe_write(struct hab_pipe_endpoint *ep,
unsigned char *p, uint32_t num_bytes)
{
struct hab_shared_buf *sh_buf = ep->tx_info.sh_buf;
uint32_t space =
(sh_buf->size - (ep->tx_info.wr_count - sh_buf->rd_count));
uint32_t count1, count2;
if (!p || num_bytes > space || num_bytes == 0) {
pr_err("****can not write to pipe p %pK to-write %d space available %d\n",
p, num_bytes, space);
return 0;
}
asm volatile("dmb ish" ::: "memory");
count1 = (num_bytes <= (sh_buf->size - ep->tx_info.index)) ? num_bytes :
(sh_buf->size - ep->tx_info.index);
count2 = num_bytes - count1;
if (count1 > 0) {
memcpy((void *)&sh_buf->data[ep->tx_info.index], p, count1);
ep->tx_info.wr_count += count1;
ep->tx_info.index += count1;
if (ep->tx_info.index >= sh_buf->size)
ep->tx_info.index = 0;
}
if (count2 > 0) {/* handle buffer wrapping */
memcpy((void *)&sh_buf->data[ep->tx_info.index],
p + count1, count2);
ep->tx_info.wr_count += count2;
ep->tx_info.index += count2;
if (ep->tx_info.index >= sh_buf->size)
ep->tx_info.index = 0;
}
return num_bytes;
}
/* Updates the write index which is shared with the other VM */
void hab_pipe_write_commit(struct hab_pipe_endpoint *ep)
{
struct hab_shared_buf *sh_buf = ep->tx_info.sh_buf;
/* Must commit data before incrementing count */
asm volatile("dmb ishst" ::: "memory");
sh_buf->wr_count = ep->tx_info.wr_count;
}
#define HAB_HEAD_CLEAR 0xCC
uint32_t hab_pipe_read(struct hab_pipe_endpoint *ep,
unsigned char *p, uint32_t size, uint32_t clear)
{
struct hab_shared_buf *sh_buf = ep->rx_info.sh_buf;
/* mb to guarantee wr_count is updated after contents are written */
uint32_t avail = sh_buf->wr_count - sh_buf->rd_count;
uint32_t count1, count2, to_read;
uint32_t index_saved = ep->rx_info.index; /* store original for retry */
if (!p || avail == 0 || size == 0)
return 0;
asm volatile("dmb ishld" ::: "memory");
/* error if available is less than size and available is not zero */
to_read = (avail < size) ? avail : size;
if (to_read < size) /* only provide exact read size, not less */
pr_err("less data available %d than requested %d\n",
avail, size);
count1 = (to_read <= (sh_buf->size - ep->rx_info.index)) ? to_read :
(sh_buf->size - ep->rx_info.index);
count2 = to_read - count1;
if (count1 > 0) {
memcpy(p, (void *)&sh_buf->data[ep->rx_info.index], count1);
ep->rx_info.index += count1;
if (ep->rx_info.index >= sh_buf->size)
ep->rx_info.index = 0;
}
if (count2 > 0) { /* handle buffer wrapping */
memcpy(p + count1, (void *)&sh_buf->data[ep->rx_info.index],
count2);
ep->rx_info.index += count2;
}
if (count1 + count2) {
struct hab_header *head = (struct hab_header *)p;
int retry_cnt = 0;
if (clear && (size == sizeof(*head))) {
retry:
if (unlikely(head->signature != 0xBEE1BEE1)) {
pr_err("hab head corruption detected at %pK buf %pK %08X %08X %08X %08X rd %d wr %d index %X saved %X retry %d\n",
head, &sh_buf->data[0],
head->id_type_size, head->session_id,
head->signature, head->sequence,
sh_buf->rd_count, sh_buf->wr_count,
ep->rx_info.index, index_saved,
retry_cnt);
if (retry_cnt++ <= 1000) {
memcpy(p, &sh_buf->data[index_saved],
count1);
if (count2)
memcpy(&p[count1],
&sh_buf->data[ep->rx_info.index - count2],
count2);
goto retry;
} else
pr_err("quit retry after %d time may fail %X %X %X %X rd %d wr %d index %X\n",
retry_cnt, head->id_type_size,
head->session_id,
head->signature,
head->sequence,
sh_buf->rd_count,
sh_buf->wr_count,
ep->rx_info.index);
}
}
/*Must commit data before incremeting count*/
asm volatile("dmb ish" ::: "memory");
sh_buf->rd_count += count1 + count2;
}
return to_read;
}
void hab_pipe_rxinfo(struct hab_pipe_endpoint *ep, uint32_t *rd_cnt,
uint32_t *wr_cnt, uint32_t *idx)
{
struct hab_shared_buf *sh_buf = ep->rx_info.sh_buf;
*idx = ep->rx_info.index;
*rd_cnt = sh_buf->rd_count;
*wr_cnt = sh_buf->wr_count;
}