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