1/* 2 * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) 3 * 4 * SPDX-License-Identifier: GPL-2.0-only 5 */ 6#define __NEED_off_t 7 8#include <sel4vchan/vmm_manager.h> 9#include <sel4vchan/vchan_component.h> 10#include <sel4vchan/vchan_sharemem.h> 11 12static libvchan_t *vchan_init(int domain, int port, int server); 13 14/* 15 Set up the vchan connection interface 16 Currently, the number of vchan connection interfaces allowed is hardcoded to 1 per component 17*/ 18libvchan_t *link_vchan_comp(libvchan_t *ctrl, camkes_vchan_con_t *vchan_com) 19{ 20 if (ctrl == NULL) { 21 return NULL; 22 } 23 24 int res; 25 ctrl->con = vchan_com; 26 /* Perform vchan component initialisation */ 27 vchan_connect_t t = { 28 .v.domain = vchan_com->source_dom_number, 29 .v.dest = ctrl->domain_num, 30 .v.port = ctrl->port_num, 31 .server = ctrl->is_server, 32 }; 33 34 res = ctrl->con->connect(t); 35 if (res < 0) { 36 free(ctrl); 37 return NULL; 38 } 39 return ctrl; 40} 41 42/* 43 Create a new vchan server instance 44*/ 45libvchan_t *libvchan_server_init(int domain, int port, size_t read_min, size_t write_min) 46{ 47 return vchan_init(domain, port, 1); 48} 49 50/* 51 Create a new vchan client instance 52*/ 53libvchan_t *libvchan_client_init(int domain, int port) 54{ 55 return vchan_init(domain, port, 0); 56} 57 58/* 59 Create a new client/server instance 60*/ 61libvchan_t *vchan_init(int domain, int port, int server) 62{ 63 libvchan_t *new_connection = malloc(sizeof(libvchan_t)); 64 if (new_connection == NULL) { 65 return NULL; 66 } 67 68 new_connection->is_server = server; 69 new_connection->server_persists = 1; 70 new_connection->blocking = 1; 71 new_connection->domain_num = domain; 72 new_connection->port_num = port; 73 74 return new_connection; 75} 76 77/* 78 Reading and writing to the vchan 79*/ 80int libvchan_write(libvchan_t *ctrl, const void *data, size_t size) 81{ 82 return libvchan_readwrite(ctrl, (void *) data, size, VCHAN_SEND, VCHAN_STREAM); 83} 84 85int libvchan_read(libvchan_t *ctrl, void *data, size_t size) 86{ 87 return libvchan_readwrite(ctrl, data, size, VCHAN_RECV, VCHAN_STREAM); 88} 89 90int libvchan_send(libvchan_t *ctrl, const void *data, size_t size) 91{ 92 return libvchan_readwrite(ctrl, (void *) data, size, VCHAN_SEND, VCHAN_NOSTREAM); 93} 94 95int libvchan_recv(libvchan_t *ctrl, void *data, size_t size) 96{ 97 return libvchan_readwrite(ctrl, data, size, VCHAN_RECV, VCHAN_NOSTREAM); 98} 99 100/* 101 Return correct buffer for given vchan read/write action 102*/ 103vchan_buf_t *get_vchan_buf(vchan_ctrl_t *args, camkes_vchan_con_t *c, int action) 104{ 105 intptr_t buf_pos = c->get_buf(*args, action); 106 /* Check that a buffer was retrieved */ 107 if (buf_pos == 0) { 108 return NULL; 109 } 110 111 if (c->data_buf == NULL) { 112 return NULL; 113 } 114 115 void *addr = c->data_buf; 116 addr += buf_pos; 117 vchan_buf_t *b = (vchan_buf_t *)(addr); 118 119 return b; 120} 121 122/* 123 Helper function 124 Allows connected components to get correct vchan buffer for read/write 125*/ 126vchan_buf_t *get_vchan_ctrl_databuf(libvchan_t *ctrl, int action) 127{ 128 vchan_ctrl_t args = { 129 .domain = ctrl->con->source_dom_number, 130 .dest = ctrl->domain_num, 131 .port = ctrl->port_num, 132 }; 133 134 return get_vchan_buf(&args, ctrl->con, action); 135} 136 137static size_t get_actionsize(int type, size_t size, vchan_buf_t *buf) 138{ 139 assert(buf != NULL); 140 size_t filled = abs(buf->write_pos - buf->read_pos); 141 if (type == VCHAN_SEND) { 142 return MIN(VCHAN_BUF_SIZE - filled, size); 143 } else { 144 return MIN(filled, size); 145 } 146} 147 148/* 149 Perform a vchan read/write action into a given buffer 150 This function is intended for non Init components, Init components have a different method 151*/ 152int libvchan_readwrite(libvchan_t *ctrl, void *data, size_t size, int cmd, int stream) 153{ 154 int *update; 155 vchan_buf_t *b = get_vchan_ctrl_databuf(ctrl, cmd); 156 if (b == NULL) { 157 return -1; 158 } 159 160 /* 161 How data is stored in a given vchan buffer 162 163 Position of data in buffer is given by 164 (either b->write_pos or b->read_pos) % VCHAN_BUF_SIZE 165 read_pos is incremented by x when x bytes are read from the buffer 166 write_pos is incremented by x when x bytes are read from the buffer 167 168 Amount of bytes in buffer is given by the difference between read_pos and write_pos 169 if write_pos > read_pos, there is data yet to be read 170 */ 171 if (cmd == VCHAN_SEND) { 172 update = &b->write_pos; 173 } else { 174 update = &b->read_pos; 175 } 176 177 178 size_t data_sz = size; 179 while (data_sz > 0) { 180 size_t call_size = MIN(data_sz, VCHAN_BUF_SIZE); 181 size_t ssize = get_actionsize(cmd, call_size, b); 182 while (ssize == 0) { 183 ctrl->con->wait(); 184 ssize = get_actionsize(cmd, call_size, b); 185 } 186 187 call_size = ssize; 188 189 /* 190 Because this buffer is circular, 191 Data may have to wrap around to the start of the buffer 192 This is achieved by doing two copies, one to buffer end 193 And one at start of buffer for remaining data 194 */ 195 off_t start = (*update % VCHAN_BUF_SIZE); 196 off_t remain = 0; 197 198 if (start + call_size > VCHAN_BUF_SIZE) { 199 remain = (start + call_size) - VCHAN_BUF_SIZE; 200 call_size -= remain; 201 } 202 203 void *dbuf = &b->sync_data; 204 205 if (cmd == VCHAN_SEND) { 206 /* 207 src = address given by function caller 208 dest = vchan dataport 209 */ 210 memcpy(dbuf + start, data, call_size); 211 memcpy(dbuf, data + call_size, remain); 212 } else { 213 /* 214 src = vchan dataport 215 dest = address given by function caller 216 */ 217 memcpy(data, ((void *) dbuf) + start, call_size); 218 memcpy(data + call_size, dbuf, remain); 219 } 220 __sync_synchronize(); 221 222 /* 223 Update either the read byte counter or the written byte counter 224 With how much was written or read 225 */ 226 *update += (call_size + remain); 227 /* 228 If stream, we have written as much data as we can in one pass. 229 Otherwise, continue to write data and block block if the buffer is full 230 */ 231 if (stream) { 232 ctrl->con->alert(); 233 return (call_size + remain); 234 } else { 235 data_sz -= (call_size + remain); 236 } 237 238 data = data + (call_size + remain); 239 ctrl->con->alert(); 240 } 241 242 return size; 243} 244 245 246/* 247 Wait for data to arrive to a component from a given vchan 248*/ 249int libvchan_wait(libvchan_t *ctrl) 250{ 251 vchan_buf_t *b = get_vchan_ctrl_databuf(ctrl, VCHAN_RECV); 252 assert(b != NULL); 253 size_t filled = abs(b->write_pos - b->read_pos); 254 while (filled == 0) { 255 ctrl->con->wait(); 256 b = get_vchan_ctrl_databuf(ctrl, VCHAN_RECV); 257 filled = abs(b->write_pos - b->read_pos); 258 } 259 260 return 0; 261} 262 263void libvchan_close(libvchan_t *ctrl) 264{ 265 vchan_connect_t t = { 266 .v.domain = ctrl->con->source_dom_number, 267 .v.dest = ctrl->domain_num, 268 .v.port = ctrl->port_num, 269 .server = ctrl->is_server, 270 }; 271 272 ctrl->con->disconnect(t); 273} 274 275int libvchan_is_open(libvchan_t *ctrl) 276{ 277 vchan_ctrl_t args = { 278 .domain = ctrl->con->source_dom_number, 279 .dest = ctrl->domain_num, 280 .port = ctrl->port_num, 281 }; 282 283 return ctrl->con->status(args); 284} 285 286/* Returns nonzero if the peer has closed connection */ 287int libvchan_is_eof(libvchan_t *ctrl) 288{ 289 if (libvchan_is_open(ctrl) != 1) { 290 return 1; 291 } 292 return 0; 293} 294 295/* 296 How much data can be read from the vchan 297*/ 298int libvchan_data_ready(libvchan_t *ctrl) 299{ 300 vchan_buf_t *b = get_vchan_ctrl_databuf(ctrl, VCHAN_RECV); 301 assert(b != NULL); 302 size_t filled = abs(b->write_pos - b->read_pos); 303 304 return filled; 305} 306 307/* 308 How much data can be written to the vchan 309*/ 310int libvchan_buffer_space(libvchan_t *ctrl) 311{ 312 if (libvchan_is_open(ctrl) != 1) { 313 return 0; 314 } 315 316 vchan_buf_t *b = get_vchan_ctrl_databuf(ctrl, VCHAN_SEND); 317 assert(b != NULL); 318 size_t filled = abs(b->write_pos - b->read_pos); 319 320 return VCHAN_BUF_SIZE - filled; 321} 322