blob: 57daf6aeb5640a0bdee559f0aa11ef9bd0581b7a [file] [log] [blame]
Varun Wadekarb3741032017-09-25 13:27:45 -07001/*
Varun Wadekar60701552020-01-10 16:52:23 -08002 * Copyright (c) 2017-2020, NVIDIA CORPORATION. All rights reserved.
Varun Wadekarb3741032017-09-25 13:27:45 -07003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <arch_helpers.h>
8#include <assert.h>
Varun Wadekar60701552020-01-10 16:52:23 -08009#include <common/debug.h>
Varun Wadekarb3741032017-09-25 13:27:45 -070010#include <errno.h>
11#include <stddef.h>
12#include <string.h>
13
14#include "ivc.h"
15
16/*
17 * IVC channel reset protocol.
18 *
19 * Each end uses its tx_channel.state to indicate its synchronization state.
20 */
21enum {
22 /*
23 * This value is zero for backwards compatibility with services that
24 * assume channels to be initially zeroed. Such channels are in an
25 * initially valid state, but cannot be asynchronously reset, and must
26 * maintain a valid state at all times.
27 *
28 * The transmitting end can enter the established state from the sync or
29 * ack state when it observes the receiving endpoint in the ack or
30 * established state, indicating that has cleared the counters in our
31 * rx_channel.
32 */
33 ivc_state_established = U(0),
34
35 /*
36 * If an endpoint is observed in the sync state, the remote endpoint is
37 * allowed to clear the counters it owns asynchronously with respect to
38 * the current endpoint. Therefore, the current endpoint is no longer
39 * allowed to communicate.
40 */
41 ivc_state_sync = U(1),
42
43 /*
44 * When the transmitting end observes the receiving end in the sync
45 * state, it can clear the w_count and r_count and transition to the ack
46 * state. If the remote endpoint observes us in the ack state, it can
47 * return to the established state once it has cleared its counters.
48 */
49 ivc_state_ack = U(2)
50};
51
52/*
53 * This structure is divided into two-cache aligned parts, the first is only
54 * written through the tx_channel pointer, while the second is only written
55 * through the rx_channel pointer. This delineates ownership of the cache lines,
56 * which is critical to performance and necessary in non-cache coherent
57 * implementations.
58 */
59struct ivc_channel_header {
60 struct {
61 /* fields owned by the transmitting end */
62 uint32_t w_count;
63 uint32_t state;
64 uint32_t w_rsvd[IVC_CHHDR_TX_FIELDS - 2];
65 };
66 struct {
67 /* fields owned by the receiving end */
68 uint32_t r_count;
69 uint32_t r_rsvd[IVC_CHHDR_RX_FIELDS - 1];
70 };
71};
72
73static inline bool ivc_channel_empty(const struct ivc *ivc,
74 volatile const struct ivc_channel_header *ch)
75{
76 /*
77 * This function performs multiple checks on the same values with
78 * security implications, so sample the counters' current values in
79 * shared memory to ensure that these checks use the same values.
80 */
81 uint32_t wr_count = ch->w_count;
82 uint32_t rd_count = ch->r_count;
83 bool ret = false;
84
85 (void)ivc;
86
87 /*
88 * Perform an over-full check to prevent denial of service attacks where
89 * a server could be easily fooled into believing that there's an
90 * extremely large number of frames ready, since receivers are not
91 * expected to check for full or over-full conditions.
92 *
93 * Although the channel isn't empty, this is an invalid case caused by
94 * a potentially malicious peer, so returning empty is safer, because it
95 * gives the impression that the channel has gone silent.
96 */
97 if (((wr_count - rd_count) > ivc->nframes) || (wr_count == rd_count)) {
98 ret = true;
99 }
100
101 return ret;
102}
103
104static inline bool ivc_channel_full(const struct ivc *ivc,
105 volatile const struct ivc_channel_header *ch)
106{
107 uint32_t wr_count = ch->w_count;
108 uint32_t rd_count = ch->r_count;
109
110 (void)ivc;
111
112 /*
113 * Invalid cases where the counters indicate that the queue is over
114 * capacity also appear full.
115 */
116 return ((wr_count - rd_count) >= ivc->nframes);
117}
118
119static inline uint32_t ivc_channel_avail_count(const struct ivc *ivc,
120 volatile const struct ivc_channel_header *ch)
121{
122 uint32_t wr_count = ch->w_count;
123 uint32_t rd_count = ch->r_count;
124
125 (void)ivc;
126
127 /*
128 * This function isn't expected to be used in scenarios where an
129 * over-full situation can lead to denial of service attacks. See the
130 * comment in ivc_channel_empty() for an explanation about special
131 * over-full considerations.
132 */
133 return (wr_count - rd_count);
134}
135
136static inline void ivc_advance_tx(struct ivc *ivc)
137{
138 ivc->tx_channel->w_count++;
139
140 if (ivc->w_pos == (ivc->nframes - (uint32_t)1U)) {
141 ivc->w_pos = 0U;
142 } else {
143 ivc->w_pos++;
144 }
145}
146
147static inline void ivc_advance_rx(struct ivc *ivc)
148{
149 ivc->rx_channel->r_count++;
150
151 if (ivc->r_pos == (ivc->nframes - (uint32_t)1U)) {
152 ivc->r_pos = 0U;
153 } else {
154 ivc->r_pos++;
155 }
156}
157
158static inline int32_t ivc_check_read(const struct ivc *ivc)
159{
160 /*
161 * tx_channel->state is set locally, so it is not synchronized with
162 * state from the remote peer. The remote peer cannot reset its
163 * transmit counters until we've acknowledged its synchronization
164 * request, so no additional synchronization is required because an
165 * asynchronous transition of rx_channel->state to ivc_state_ack is not
166 * allowed.
167 */
168 if (ivc->tx_channel->state != ivc_state_established) {
169 return -ECONNRESET;
170 }
171
172 /*
173 * Avoid unnecessary invalidations when performing repeated accesses to
174 * an IVC channel by checking the old queue pointers first.
175 * Synchronization is only necessary when these pointers indicate empty
176 * or full.
177 */
178 if (!ivc_channel_empty(ivc, ivc->rx_channel)) {
179 return 0;
180 }
181
182 return ivc_channel_empty(ivc, ivc->rx_channel) ? -ENOMEM : 0;
183}
184
185static inline int32_t ivc_check_write(const struct ivc *ivc)
186{
187 if (ivc->tx_channel->state != ivc_state_established) {
188 return -ECONNRESET;
189 }
190
191 if (!ivc_channel_full(ivc, ivc->tx_channel)) {
192 return 0;
193 }
194
195 return ivc_channel_full(ivc, ivc->tx_channel) ? -ENOMEM : 0;
196}
197
198bool tegra_ivc_can_read(const struct ivc *ivc)
199{
200 return ivc_check_read(ivc) == 0;
201}
202
203bool tegra_ivc_can_write(const struct ivc *ivc)
204{
205 return ivc_check_write(ivc) == 0;
206}
207
208bool tegra_ivc_tx_empty(const struct ivc *ivc)
209{
210 return ivc_channel_empty(ivc, ivc->tx_channel);
211}
212
213static inline uintptr_t calc_frame_offset(uint32_t frame_index,
214 uint32_t frame_size, uint32_t frame_offset)
215{
216 return ((uintptr_t)frame_index * (uintptr_t)frame_size) +
217 (uintptr_t)frame_offset;
218}
219
220static void *ivc_frame_pointer(const struct ivc *ivc,
221 volatile const struct ivc_channel_header *ch,
222 uint32_t frame)
223{
224 assert(frame < ivc->nframes);
225 return (void *)((uintptr_t)(&ch[1]) +
226 calc_frame_offset(frame, ivc->frame_size, 0));
227}
228
229int32_t tegra_ivc_read(struct ivc *ivc, void *buf, size_t max_read)
230{
231 const void *src;
232 int32_t result;
233
234 if (buf == NULL) {
235 return -EINVAL;
236 }
237
238 if (max_read > ivc->frame_size) {
239 return -E2BIG;
240 }
241
242 result = ivc_check_read(ivc);
243 if (result != 0) {
244 return result;
245 }
246
247 /*
248 * Order observation of w_pos potentially indicating new data before
249 * data read.
250 */
251 dmbish();
252
253 src = ivc_frame_pointer(ivc, ivc->rx_channel, ivc->r_pos);
254
255 (void)memcpy(buf, src, max_read);
256
257 ivc_advance_rx(ivc);
258
259 /*
260 * Ensure our write to r_pos occurs before our read from w_pos.
261 */
262 dmbish();
263
264 /*
265 * Notify only upon transition from full to non-full.
266 * The available count can only asynchronously increase, so the
267 * worst possible side-effect will be a spurious notification.
268 */
269 if (ivc_channel_avail_count(ivc, ivc->rx_channel) == (ivc->nframes - (uint32_t)1U)) {
270 ivc->notify(ivc);
271 }
272
273 return (int32_t)max_read;
274}
275
276/* directly peek at the next frame rx'ed */
277void *tegra_ivc_read_get_next_frame(const struct ivc *ivc)
278{
279 if (ivc_check_read(ivc) != 0) {
280 return NULL;
281 }
282
283 /*
284 * Order observation of w_pos potentially indicating new data before
285 * data read.
286 */
287 dmbld();
288
289 return ivc_frame_pointer(ivc, ivc->rx_channel, ivc->r_pos);
290}
291
292int32_t tegra_ivc_read_advance(struct ivc *ivc)
293{
294 /*
295 * No read barriers or synchronization here: the caller is expected to
296 * have already observed the channel non-empty. This check is just to
297 * catch programming errors.
298 */
299 int32_t result = ivc_check_read(ivc);
300 if (result != 0) {
301 return result;
302 }
303
304 ivc_advance_rx(ivc);
305
306 /*
307 * Ensure our write to r_pos occurs before our read from w_pos.
308 */
309 dmbish();
310
311 /*
312 * Notify only upon transition from full to non-full.
313 * The available count can only asynchronously increase, so the
314 * worst possible side-effect will be a spurious notification.
315 */
316 if (ivc_channel_avail_count(ivc, ivc->rx_channel) == (ivc->nframes - (uint32_t)1U)) {
317 ivc->notify(ivc);
318 }
319
320 return 0;
321}
322
323int32_t tegra_ivc_write(struct ivc *ivc, const void *buf, size_t size)
324{
325 void *p;
326 int32_t result;
327
328 if ((buf == NULL) || (ivc == NULL)) {
329 return -EINVAL;
330 }
331
332 if (size > ivc->frame_size) {
333 return -E2BIG;
334 }
335
336 result = ivc_check_write(ivc);
337 if (result != 0) {
338 return result;
339 }
340
341 p = ivc_frame_pointer(ivc, ivc->tx_channel, ivc->w_pos);
342
343 (void)memset(p, 0, ivc->frame_size);
344 (void)memcpy(p, buf, size);
345
346 /*
347 * Ensure that updated data is visible before the w_pos counter
348 * indicates that it is ready.
349 */
350 dmbst();
351
352 ivc_advance_tx(ivc);
353
354 /*
355 * Ensure our write to w_pos occurs before our read from r_pos.
356 */
357 dmbish();
358
359 /*
360 * Notify only upon transition from empty to non-empty.
361 * The available count can only asynchronously decrease, so the
362 * worst possible side-effect will be a spurious notification.
363 */
364 if (ivc_channel_avail_count(ivc, ivc->tx_channel) == 1U) {
365 ivc->notify(ivc);
366 }
367
368 return (int32_t)size;
369}
370
371/* directly poke at the next frame to be tx'ed */
372void *tegra_ivc_write_get_next_frame(const struct ivc *ivc)
373{
374 if (ivc_check_write(ivc) != 0) {
375 return NULL;
376 }
377
378 return ivc_frame_pointer(ivc, ivc->tx_channel, ivc->w_pos);
379}
380
381/* advance the tx buffer */
382int32_t tegra_ivc_write_advance(struct ivc *ivc)
383{
384 int32_t result = ivc_check_write(ivc);
385
386 if (result != 0) {
387 return result;
388 }
389
390 /*
391 * Order any possible stores to the frame before update of w_pos.
392 */
393 dmbst();
394
395 ivc_advance_tx(ivc);
396
397 /*
398 * Ensure our write to w_pos occurs before our read from r_pos.
399 */
400 dmbish();
401
402 /*
403 * Notify only upon transition from empty to non-empty.
404 * The available count can only asynchronously decrease, so the
405 * worst possible side-effect will be a spurious notification.
406 */
407 if (ivc_channel_avail_count(ivc, ivc->tx_channel) == (uint32_t)1U) {
408 ivc->notify(ivc);
409 }
410
411 return 0;
412}
413
414void tegra_ivc_channel_reset(const struct ivc *ivc)
415{
416 ivc->tx_channel->state = ivc_state_sync;
417 ivc->notify(ivc);
418}
419
420/*
421 * ===============================================================
422 * IVC State Transition Table - see tegra_ivc_channel_notified()
423 * ===============================================================
424 *
425 * local remote action
426 * ----- ------ -----------------------------------
427 * SYNC EST <none>
428 * SYNC ACK reset counters; move to EST; notify
429 * SYNC SYNC reset counters; move to ACK; notify
430 * ACK EST move to EST; notify
431 * ACK ACK move to EST; notify
432 * ACK SYNC reset counters; move to ACK; notify
433 * EST EST <none>
434 * EST ACK <none>
435 * EST SYNC reset counters; move to ACK; notify
436 *
437 * ===============================================================
438 */
439int32_t tegra_ivc_channel_notified(struct ivc *ivc)
440{
441 uint32_t peer_state;
442
443 /* Copy the receiver's state out of shared memory. */
444 peer_state = ivc->rx_channel->state;
445
446 if (peer_state == (uint32_t)ivc_state_sync) {
447 /*
448 * Order observation of ivc_state_sync before stores clearing
449 * tx_channel.
450 */
451 dmbld();
452
453 /*
454 * Reset tx_channel counters. The remote end is in the SYNC
455 * state and won't make progress until we change our state,
456 * so the counters are not in use at this time.
457 */
458 ivc->tx_channel->w_count = 0U;
459 ivc->rx_channel->r_count = 0U;
460
461 ivc->w_pos = 0U;
462 ivc->r_pos = 0U;
463
464 /*
465 * Ensure that counters appear cleared before new state can be
466 * observed.
467 */
468 dmbst();
469
470 /*
471 * Move to ACK state. We have just cleared our counters, so it
472 * is now safe for the remote end to start using these values.
473 */
474 ivc->tx_channel->state = ivc_state_ack;
475
476 /*
477 * Notify remote end to observe state transition.
478 */
479 ivc->notify(ivc);
480
481 } else if ((ivc->tx_channel->state == (uint32_t)ivc_state_sync) &&
482 (peer_state == (uint32_t)ivc_state_ack)) {
483 /*
484 * Order observation of ivc_state_sync before stores clearing
485 * tx_channel.
486 */
487 dmbld();
488
489 /*
490 * Reset tx_channel counters. The remote end is in the ACK
491 * state and won't make progress until we change our state,
492 * so the counters are not in use at this time.
493 */
494 ivc->tx_channel->w_count = 0U;
495 ivc->rx_channel->r_count = 0U;
496
497 ivc->w_pos = 0U;
498 ivc->r_pos = 0U;
499
500 /*
501 * Ensure that counters appear cleared before new state can be
502 * observed.
503 */
504 dmbst();
505
506 /*
507 * Move to ESTABLISHED state. We know that the remote end has
508 * already cleared its counters, so it is safe to start
509 * writing/reading on this channel.
510 */
511 ivc->tx_channel->state = ivc_state_established;
512
513 /*
514 * Notify remote end to observe state transition.
515 */
516 ivc->notify(ivc);
517
518 } else if (ivc->tx_channel->state == (uint32_t)ivc_state_ack) {
519 /*
520 * At this point, we have observed the peer to be in either
521 * the ACK or ESTABLISHED state. Next, order observation of
522 * peer state before storing to tx_channel.
523 */
524 dmbld();
525
526 /*
527 * Move to ESTABLISHED state. We know that we have previously
528 * cleared our counters, and we know that the remote end has
529 * cleared its counters, so it is safe to start writing/reading
530 * on this channel.
531 */
532 ivc->tx_channel->state = ivc_state_established;
533
534 /*
535 * Notify remote end to observe state transition.
536 */
537 ivc->notify(ivc);
538
539 } else {
540 /*
541 * There is no need to handle any further action. Either the
542 * channel is already fully established, or we are waiting for
543 * the remote end to catch up with our current state. Refer
544 * to the diagram in "IVC State Transition Table" above.
545 */
546 }
547
548 return ((ivc->tx_channel->state == (uint32_t)ivc_state_established) ? 0 : -EAGAIN);
549}
550
551size_t tegra_ivc_align(size_t size)
552{
553 return (size + (IVC_ALIGN - 1U)) & ~(IVC_ALIGN - 1U);
554}
555
556size_t tegra_ivc_total_queue_size(size_t queue_size)
557{
558 if ((queue_size & (IVC_ALIGN - 1U)) != 0U) {
559 ERROR("queue_size (%d) must be %d-byte aligned\n",
560 (int32_t)queue_size, IVC_ALIGN);
561 return 0;
562 }
563 return queue_size + sizeof(struct ivc_channel_header);
564}
565
566static int32_t check_ivc_params(uintptr_t queue_base1, uintptr_t queue_base2,
567 uint32_t nframes, uint32_t frame_size)
568{
569 assert((offsetof(struct ivc_channel_header, w_count)
570 & (IVC_ALIGN - 1U)) == 0U);
571 assert((offsetof(struct ivc_channel_header, r_count)
572 & (IVC_ALIGN - 1U)) == 0U);
573 assert((sizeof(struct ivc_channel_header) & (IVC_ALIGN - 1U)) == 0U);
574
575 if (((uint64_t)nframes * (uint64_t)frame_size) >= 0x100000000ULL) {
576 ERROR("nframes * frame_size overflows\n");
577 return -EINVAL;
578 }
579
580 /*
581 * The headers must at least be aligned enough for counters
582 * to be accessed atomically.
583 */
584 if ((queue_base1 & (IVC_ALIGN - 1U)) != 0U) {
585 ERROR("ivc channel start not aligned: %lx\n", queue_base1);
586 return -EINVAL;
587 }
588 if ((queue_base2 & (IVC_ALIGN - 1U)) != 0U) {
589 ERROR("ivc channel start not aligned: %lx\n", queue_base2);
590 return -EINVAL;
591 }
592
593 if ((frame_size & (IVC_ALIGN - 1U)) != 0U) {
594 ERROR("frame size not adequately aligned: %u\n",
595 frame_size);
596 return -EINVAL;
597 }
598
599 if (queue_base1 < queue_base2) {
600 if ((queue_base1 + ((uint64_t)frame_size * nframes)) > queue_base2) {
601 ERROR("queue regions overlap: %lx + %x, %x\n",
602 queue_base1, frame_size,
603 frame_size * nframes);
604 return -EINVAL;
605 }
606 } else {
607 if ((queue_base2 + ((uint64_t)frame_size * nframes)) > queue_base1) {
608 ERROR("queue regions overlap: %lx + %x, %x\n",
609 queue_base2, frame_size,
610 frame_size * nframes);
611 return -EINVAL;
612 }
613 }
614
615 return 0;
616}
617
618int32_t tegra_ivc_init(struct ivc *ivc, uintptr_t rx_base, uintptr_t tx_base,
619 uint32_t nframes, uint32_t frame_size,
620 ivc_notify_function notify)
621{
622 int32_t result;
623
624 /* sanity check input params */
625 if ((ivc == NULL) || (notify == NULL)) {
626 return -EINVAL;
627 }
628
629 result = check_ivc_params(rx_base, tx_base, nframes, frame_size);
630 if (result != 0) {
631 return result;
632 }
633
634 /*
635 * All sizes that can be returned by communication functions should
636 * fit in a 32-bit integer.
637 */
638 if (frame_size > (1u << 31)) {
639 return -E2BIG;
640 }
641
642 ivc->rx_channel = (struct ivc_channel_header *)rx_base;
643 ivc->tx_channel = (struct ivc_channel_header *)tx_base;
644 ivc->notify = notify;
645 ivc->frame_size = frame_size;
646 ivc->nframes = nframes;
647 ivc->w_pos = 0U;
648 ivc->r_pos = 0U;
649
650 INFO("%s: done\n", __func__);
651
652 return 0;
653}