• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2017-2020, NVIDIA CORPORATION. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <arch_helpers.h>
8 #include <assert.h>
9 #include <common/debug.h>
10 #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  */
21 enum {
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  */
59 struct 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 
ivc_channel_empty(const struct ivc * ivc,volatile const struct ivc_channel_header * ch)73 static 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 
ivc_channel_full(const struct ivc * ivc,volatile const struct ivc_channel_header * ch)104 static 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 
ivc_channel_avail_count(const struct ivc * ivc,volatile const struct ivc_channel_header * ch)119 static 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 
ivc_advance_tx(struct ivc * ivc)136 static 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 
ivc_advance_rx(struct ivc * ivc)147 static 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 
ivc_check_read(const struct ivc * ivc)158 static 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 
ivc_check_write(const struct ivc * ivc)185 static 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 
tegra_ivc_can_read(const struct ivc * ivc)198 bool tegra_ivc_can_read(const struct ivc *ivc)
199 {
200 	return ivc_check_read(ivc) == 0;
201 }
202 
tegra_ivc_can_write(const struct ivc * ivc)203 bool tegra_ivc_can_write(const struct ivc *ivc)
204 {
205 	return ivc_check_write(ivc) == 0;
206 }
207 
tegra_ivc_tx_empty(const struct ivc * ivc)208 bool tegra_ivc_tx_empty(const struct ivc *ivc)
209 {
210 	return ivc_channel_empty(ivc, ivc->tx_channel);
211 }
212 
calc_frame_offset(uint32_t frame_index,uint32_t frame_size,uint32_t frame_offset)213 static 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 
ivc_frame_pointer(const struct ivc * ivc,volatile const struct ivc_channel_header * ch,uint32_t frame)220 static 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 
tegra_ivc_read(struct ivc * ivc,void * buf,size_t max_read)229 int32_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 */
tegra_ivc_read_get_next_frame(const struct ivc * ivc)277 void *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 
tegra_ivc_read_advance(struct ivc * ivc)292 int32_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 
tegra_ivc_write(struct ivc * ivc,const void * buf,size_t size)323 int32_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 */
tegra_ivc_write_get_next_frame(const struct ivc * ivc)372 void *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 */
tegra_ivc_write_advance(struct ivc * ivc)382 int32_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 
tegra_ivc_channel_reset(const struct ivc * ivc)414 void 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  */
tegra_ivc_channel_notified(struct ivc * ivc)439 int32_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 
tegra_ivc_align(size_t size)551 size_t tegra_ivc_align(size_t size)
552 {
553 	return (size + (IVC_ALIGN - 1U)) & ~(IVC_ALIGN - 1U);
554 }
555 
tegra_ivc_total_queue_size(size_t queue_size)556 size_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 
check_ivc_params(uintptr_t queue_base1,uintptr_t queue_base2,uint32_t nframes,uint32_t frame_size)566 static 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 
tegra_ivc_init(struct ivc * ivc,uintptr_t rx_base,uintptr_t tx_base,uint32_t nframes,uint32_t frame_size,ivc_notify_function notify)618 int32_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 }
654