xref: /openbmc/u-boot/arch/arm/mach-tegra/ivc.c (revision e8f80a5a)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2016, NVIDIA CORPORATION.
4  */
5 
6 #include <common.h>
7 #include <asm/io.h>
8 #include <asm/arch-tegra/ivc.h>
9 
10 #define TEGRA_IVC_ALIGN 64
11 
12 /*
13  * IVC channel reset protocol.
14  *
15  * Each end uses its tx_channel.state to indicate its synchronization state.
16  */
17 enum ivc_state {
18 	/*
19 	 * This value is zero for backwards compatibility with services that
20 	 * assume channels to be initially zeroed. Such channels are in an
21 	 * initially valid state, but cannot be asynchronously reset, and must
22 	 * maintain a valid state at all times.
23 	 *
24 	 * The transmitting end can enter the established state from the sync or
25 	 * ack state when it observes the receiving endpoint in the ack or
26 	 * established state, indicating that has cleared the counters in our
27 	 * rx_channel.
28 	 */
29 	ivc_state_established = 0,
30 
31 	/*
32 	 * If an endpoint is observed in the sync state, the remote endpoint is
33 	 * allowed to clear the counters it owns asynchronously with respect to
34 	 * the current endpoint. Therefore, the current endpoint is no longer
35 	 * allowed to communicate.
36 	 */
37 	ivc_state_sync,
38 
39 	/*
40 	 * When the transmitting end observes the receiving end in the sync
41 	 * state, it can clear the w_count and r_count and transition to the ack
42 	 * state. If the remote endpoint observes us in the ack state, it can
43 	 * return to the established state once it has cleared its counters.
44 	 */
45 	ivc_state_ack
46 };
47 
48 /*
49  * This structure is divided into two-cache aligned parts, the first is only
50  * written through the tx_channel pointer, while the second is only written
51  * through the rx_channel pointer. This delineates ownership of the cache lines,
52  * which is critical to performance and necessary in non-cache coherent
53  * implementations.
54  */
55 struct tegra_ivc_channel_header {
56 	union {
57 		/* fields owned by the transmitting end */
58 		struct {
59 			uint32_t w_count;
60 			uint32_t state;
61 		};
62 		uint8_t w_align[TEGRA_IVC_ALIGN];
63 	};
64 	union {
65 		/* fields owned by the receiving end */
66 		uint32_t r_count;
67 		uint8_t r_align[TEGRA_IVC_ALIGN];
68 	};
69 };
70 
tegra_ivc_invalidate_counter(struct tegra_ivc * ivc,struct tegra_ivc_channel_header * h,ulong offset)71 static inline void tegra_ivc_invalidate_counter(struct tegra_ivc *ivc,
72 					struct tegra_ivc_channel_header *h,
73 					ulong offset)
74 {
75 	ulong base = ((ulong)h) + offset;
76 	invalidate_dcache_range(base, base + TEGRA_IVC_ALIGN);
77 }
78 
tegra_ivc_flush_counter(struct tegra_ivc * ivc,struct tegra_ivc_channel_header * h,ulong offset)79 static inline void tegra_ivc_flush_counter(struct tegra_ivc *ivc,
80 					   struct tegra_ivc_channel_header *h,
81 					   ulong offset)
82 {
83 	ulong base = ((ulong)h) + offset;
84 	flush_dcache_range(base, base + TEGRA_IVC_ALIGN);
85 }
86 
tegra_ivc_frame_addr(struct tegra_ivc * ivc,struct tegra_ivc_channel_header * h,uint32_t frame)87 static inline ulong tegra_ivc_frame_addr(struct tegra_ivc *ivc,
88 					 struct tegra_ivc_channel_header *h,
89 					 uint32_t frame)
90 {
91 	BUG_ON(frame >= ivc->nframes);
92 
93 	return ((ulong)h) + sizeof(struct tegra_ivc_channel_header) +
94 	       (ivc->frame_size * frame);
95 }
96 
tegra_ivc_frame_pointer(struct tegra_ivc * ivc,struct tegra_ivc_channel_header * ch,uint32_t frame)97 static inline void *tegra_ivc_frame_pointer(struct tegra_ivc *ivc,
98 					    struct tegra_ivc_channel_header *ch,
99 					    uint32_t frame)
100 {
101 	return (void *)tegra_ivc_frame_addr(ivc, ch, frame);
102 }
103 
tegra_ivc_invalidate_frame(struct tegra_ivc * ivc,struct tegra_ivc_channel_header * h,unsigned frame)104 static inline void tegra_ivc_invalidate_frame(struct tegra_ivc *ivc,
105 					struct tegra_ivc_channel_header *h,
106 					unsigned frame)
107 {
108 	ulong base = tegra_ivc_frame_addr(ivc, h, frame);
109 	invalidate_dcache_range(base, base + ivc->frame_size);
110 }
111 
tegra_ivc_flush_frame(struct tegra_ivc * ivc,struct tegra_ivc_channel_header * h,unsigned frame)112 static inline void tegra_ivc_flush_frame(struct tegra_ivc *ivc,
113 					 struct tegra_ivc_channel_header *h,
114 					 unsigned frame)
115 {
116 	ulong base = tegra_ivc_frame_addr(ivc, h, frame);
117 	flush_dcache_range(base, base + ivc->frame_size);
118 }
119 
tegra_ivc_channel_empty(struct tegra_ivc * ivc,struct tegra_ivc_channel_header * ch)120 static inline int tegra_ivc_channel_empty(struct tegra_ivc *ivc,
121 					  struct tegra_ivc_channel_header *ch)
122 {
123 	/*
124 	 * This function performs multiple checks on the same values with
125 	 * security implications, so create snapshots with ACCESS_ONCE() to
126 	 * ensure that these checks use the same values.
127 	 */
128 	uint32_t w_count = ACCESS_ONCE(ch->w_count);
129 	uint32_t r_count = ACCESS_ONCE(ch->r_count);
130 
131 	/*
132 	 * Perform an over-full check to prevent denial of service attacks where
133 	 * a server could be easily fooled into believing that there's an
134 	 * extremely large number of frames ready, since receivers are not
135 	 * expected to check for full or over-full conditions.
136 	 *
137 	 * Although the channel isn't empty, this is an invalid case caused by
138 	 * a potentially malicious peer, so returning empty is safer, because it
139 	 * gives the impression that the channel has gone silent.
140 	 */
141 	if (w_count - r_count > ivc->nframes)
142 		return 1;
143 
144 	return w_count == r_count;
145 }
146 
tegra_ivc_channel_full(struct tegra_ivc * ivc,struct tegra_ivc_channel_header * ch)147 static inline int tegra_ivc_channel_full(struct tegra_ivc *ivc,
148 					 struct tegra_ivc_channel_header *ch)
149 {
150 	/*
151 	 * Invalid cases where the counters indicate that the queue is over
152 	 * capacity also appear full.
153 	 */
154 	return (ACCESS_ONCE(ch->w_count) - ACCESS_ONCE(ch->r_count)) >=
155 	       ivc->nframes;
156 }
157 
tegra_ivc_advance_rx(struct tegra_ivc * ivc)158 static inline void tegra_ivc_advance_rx(struct tegra_ivc *ivc)
159 {
160 	ACCESS_ONCE(ivc->rx_channel->r_count) =
161 			ACCESS_ONCE(ivc->rx_channel->r_count) + 1;
162 
163 	if (ivc->r_pos == ivc->nframes - 1)
164 		ivc->r_pos = 0;
165 	else
166 		ivc->r_pos++;
167 }
168 
tegra_ivc_advance_tx(struct tegra_ivc * ivc)169 static inline void tegra_ivc_advance_tx(struct tegra_ivc *ivc)
170 {
171 	ACCESS_ONCE(ivc->tx_channel->w_count) =
172 			ACCESS_ONCE(ivc->tx_channel->w_count) + 1;
173 
174 	if (ivc->w_pos == ivc->nframes - 1)
175 		ivc->w_pos = 0;
176 	else
177 		ivc->w_pos++;
178 }
179 
tegra_ivc_check_read(struct tegra_ivc * ivc)180 static inline int tegra_ivc_check_read(struct tegra_ivc *ivc)
181 {
182 	ulong offset;
183 
184 	/*
185 	 * tx_channel->state is set locally, so it is not synchronized with
186 	 * state from the remote peer. The remote peer cannot reset its
187 	 * transmit counters until we've acknowledged its synchronization
188 	 * request, so no additional synchronization is required because an
189 	 * asynchronous transition of rx_channel->state to ivc_state_ack is not
190 	 * allowed.
191 	 */
192 	if (ivc->tx_channel->state != ivc_state_established)
193 		return -ECONNRESET;
194 
195 	/*
196 	 * Avoid unnecessary invalidations when performing repeated accesses to
197 	 * an IVC channel by checking the old queue pointers first.
198 	 * Synchronization is only necessary when these pointers indicate empty
199 	 * or full.
200 	 */
201 	if (!tegra_ivc_channel_empty(ivc, ivc->rx_channel))
202 		return 0;
203 
204 	offset = offsetof(struct tegra_ivc_channel_header, w_count);
205 	tegra_ivc_invalidate_counter(ivc, ivc->rx_channel, offset);
206 	return tegra_ivc_channel_empty(ivc, ivc->rx_channel) ? -ENOMEM : 0;
207 }
208 
tegra_ivc_check_write(struct tegra_ivc * ivc)209 static inline int tegra_ivc_check_write(struct tegra_ivc *ivc)
210 {
211 	ulong offset;
212 
213 	if (ivc->tx_channel->state != ivc_state_established)
214 		return -ECONNRESET;
215 
216 	if (!tegra_ivc_channel_full(ivc, ivc->tx_channel))
217 		return 0;
218 
219 	offset = offsetof(struct tegra_ivc_channel_header, r_count);
220 	tegra_ivc_invalidate_counter(ivc, ivc->tx_channel, offset);
221 	return tegra_ivc_channel_full(ivc, ivc->tx_channel) ? -ENOMEM : 0;
222 }
223 
tegra_ivc_channel_avail_count(struct tegra_ivc * ivc,struct tegra_ivc_channel_header * ch)224 static inline uint32_t tegra_ivc_channel_avail_count(struct tegra_ivc *ivc,
225 	struct tegra_ivc_channel_header *ch)
226 {
227 	/*
228 	 * This function isn't expected to be used in scenarios where an
229 	 * over-full situation can lead to denial of service attacks. See the
230 	 * comment in tegra_ivc_channel_empty() for an explanation about
231 	 * special over-full considerations.
232 	 */
233 	return ACCESS_ONCE(ch->w_count) - ACCESS_ONCE(ch->r_count);
234 }
235 
tegra_ivc_read_get_next_frame(struct tegra_ivc * ivc,void ** frame)236 int tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc, void **frame)
237 {
238 	int result = tegra_ivc_check_read(ivc);
239 	if (result < 0)
240 		return result;
241 
242 	/*
243 	 * Order observation of w_pos potentially indicating new data before
244 	 * data read.
245 	 */
246 	mb();
247 
248 	tegra_ivc_invalidate_frame(ivc, ivc->rx_channel, ivc->r_pos);
249 	*frame = tegra_ivc_frame_pointer(ivc, ivc->rx_channel, ivc->r_pos);
250 
251 	return 0;
252 }
253 
tegra_ivc_read_advance(struct tegra_ivc * ivc)254 int tegra_ivc_read_advance(struct tegra_ivc *ivc)
255 {
256 	ulong offset;
257 	int result;
258 
259 	/*
260 	 * No read barriers or synchronization here: the caller is expected to
261 	 * have already observed the channel non-empty. This check is just to
262 	 * catch programming errors.
263 	 */
264 	result = tegra_ivc_check_read(ivc);
265 	if (result)
266 		return result;
267 
268 	tegra_ivc_advance_rx(ivc);
269 	offset = offsetof(struct tegra_ivc_channel_header, r_count);
270 	tegra_ivc_flush_counter(ivc, ivc->rx_channel, offset);
271 
272 	/*
273 	 * Ensure our write to r_pos occurs before our read from w_pos.
274 	 */
275 	mb();
276 
277 	offset = offsetof(struct tegra_ivc_channel_header, w_count);
278 	tegra_ivc_invalidate_counter(ivc, ivc->rx_channel, offset);
279 
280 	if (tegra_ivc_channel_avail_count(ivc, ivc->rx_channel) ==
281 	    ivc->nframes - 1)
282 		ivc->notify(ivc);
283 
284 	return 0;
285 }
286 
tegra_ivc_write_get_next_frame(struct tegra_ivc * ivc,void ** frame)287 int tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc, void **frame)
288 {
289 	int result = tegra_ivc_check_write(ivc);
290 	if (result)
291 		return result;
292 
293 	*frame = tegra_ivc_frame_pointer(ivc, ivc->tx_channel, ivc->w_pos);
294 
295 	return 0;
296 }
297 
tegra_ivc_write_advance(struct tegra_ivc * ivc)298 int tegra_ivc_write_advance(struct tegra_ivc *ivc)
299 {
300 	ulong offset;
301 	int result;
302 
303 	result = tegra_ivc_check_write(ivc);
304 	if (result)
305 		return result;
306 
307 	tegra_ivc_flush_frame(ivc, ivc->tx_channel, ivc->w_pos);
308 
309 	/*
310 	 * Order any possible stores to the frame before update of w_pos.
311 	 */
312 	mb();
313 
314 	tegra_ivc_advance_tx(ivc);
315 	offset = offsetof(struct tegra_ivc_channel_header, w_count);
316 	tegra_ivc_flush_counter(ivc, ivc->tx_channel, offset);
317 
318 	/*
319 	 * Ensure our write to w_pos occurs before our read from r_pos.
320 	 */
321 	mb();
322 
323 	offset = offsetof(struct tegra_ivc_channel_header, r_count);
324 	tegra_ivc_invalidate_counter(ivc, ivc->tx_channel, offset);
325 
326 	if (tegra_ivc_channel_avail_count(ivc, ivc->tx_channel) == 1)
327 		ivc->notify(ivc);
328 
329 	return 0;
330 }
331 
332 /*
333  * ===============================================================
334  *  IVC State Transition Table - see tegra_ivc_channel_notified()
335  * ===============================================================
336  *
337  *	local	remote	action
338  *	-----	------	-----------------------------------
339  *	SYNC	EST	<none>
340  *	SYNC	ACK	reset counters; move to EST; notify
341  *	SYNC	SYNC	reset counters; move to ACK; notify
342  *	ACK	EST	move to EST; notify
343  *	ACK	ACK	move to EST; notify
344  *	ACK	SYNC	reset counters; move to ACK; notify
345  *	EST	EST	<none>
346  *	EST	ACK	<none>
347  *	EST	SYNC	reset counters; move to ACK; notify
348  *
349  * ===============================================================
350  */
tegra_ivc_channel_notified(struct tegra_ivc * ivc)351 int tegra_ivc_channel_notified(struct tegra_ivc *ivc)
352 {
353 	ulong offset;
354 	enum ivc_state peer_state;
355 
356 	/* Copy the receiver's state out of shared memory. */
357 	offset = offsetof(struct tegra_ivc_channel_header, w_count);
358 	tegra_ivc_invalidate_counter(ivc, ivc->rx_channel, offset);
359 	peer_state = ACCESS_ONCE(ivc->rx_channel->state);
360 
361 	if (peer_state == ivc_state_sync) {
362 		/*
363 		 * Order observation of ivc_state_sync before stores clearing
364 		 * tx_channel.
365 		 */
366 		mb();
367 
368 		/*
369 		 * Reset tx_channel counters. The remote end is in the SYNC
370 		 * state and won't make progress until we change our state,
371 		 * so the counters are not in use at this time.
372 		 */
373 		ivc->tx_channel->w_count = 0;
374 		ivc->rx_channel->r_count = 0;
375 
376 		ivc->w_pos = 0;
377 		ivc->r_pos = 0;
378 
379 		/*
380 		 * Ensure that counters appear cleared before new state can be
381 		 * observed.
382 		 */
383 		mb();
384 
385 		/*
386 		 * Move to ACK state. We have just cleared our counters, so it
387 		 * is now safe for the remote end to start using these values.
388 		 */
389 		ivc->tx_channel->state = ivc_state_ack;
390 		offset = offsetof(struct tegra_ivc_channel_header, w_count);
391 		tegra_ivc_flush_counter(ivc, ivc->tx_channel, offset);
392 
393 		/*
394 		 * Notify remote end to observe state transition.
395 		 */
396 		ivc->notify(ivc);
397 	} else if (ivc->tx_channel->state == ivc_state_sync &&
398 			peer_state == ivc_state_ack) {
399 		/*
400 		 * Order observation of ivc_state_sync before stores clearing
401 		 * tx_channel.
402 		 */
403 		mb();
404 
405 		/*
406 		 * Reset tx_channel counters. The remote end is in the ACK
407 		 * state and won't make progress until we change our state,
408 		 * so the counters are not in use at this time.
409 		 */
410 		ivc->tx_channel->w_count = 0;
411 		ivc->rx_channel->r_count = 0;
412 
413 		ivc->w_pos = 0;
414 		ivc->r_pos = 0;
415 
416 		/*
417 		 * Ensure that counters appear cleared before new state can be
418 		 * observed.
419 		 */
420 		mb();
421 
422 		/*
423 		 * Move to ESTABLISHED state. We know that the remote end has
424 		 * already cleared its counters, so it is safe to start
425 		 * writing/reading on this channel.
426 		 */
427 		ivc->tx_channel->state = ivc_state_established;
428 		offset = offsetof(struct tegra_ivc_channel_header, w_count);
429 		tegra_ivc_flush_counter(ivc, ivc->tx_channel, offset);
430 
431 		/*
432 		 * Notify remote end to observe state transition.
433 		 */
434 		ivc->notify(ivc);
435 	} else if (ivc->tx_channel->state == ivc_state_ack) {
436 		/*
437 		 * At this point, we have observed the peer to be in either
438 		 * the ACK or ESTABLISHED state. Next, order observation of
439 		 * peer state before storing to tx_channel.
440 		 */
441 		mb();
442 
443 		/*
444 		 * Move to ESTABLISHED state. We know that we have previously
445 		 * cleared our counters, and we know that the remote end has
446 		 * cleared its counters, so it is safe to start writing/reading
447 		 * on this channel.
448 		 */
449 		ivc->tx_channel->state = ivc_state_established;
450 		offset = offsetof(struct tegra_ivc_channel_header, w_count);
451 		tegra_ivc_flush_counter(ivc, ivc->tx_channel, offset);
452 
453 		/*
454 		 * Notify remote end to observe state transition.
455 		 */
456 		ivc->notify(ivc);
457 	} else {
458 		/*
459 		 * There is no need to handle any further action. Either the
460 		 * channel is already fully established, or we are waiting for
461 		 * the remote end to catch up with our current state. Refer
462 		 * to the diagram in "IVC State Transition Table" above.
463 		 */
464 	}
465 
466 	if (ivc->tx_channel->state != ivc_state_established)
467 		return -EAGAIN;
468 
469 	return 0;
470 }
471 
tegra_ivc_channel_reset(struct tegra_ivc * ivc)472 void tegra_ivc_channel_reset(struct tegra_ivc *ivc)
473 {
474 	ulong offset;
475 
476 	ivc->tx_channel->state = ivc_state_sync;
477 	offset = offsetof(struct tegra_ivc_channel_header, w_count);
478 	tegra_ivc_flush_counter(ivc, ivc->tx_channel, offset);
479 	ivc->notify(ivc);
480 }
481 
check_ivc_params(ulong qbase1,ulong qbase2,uint32_t nframes,uint32_t frame_size)482 static int check_ivc_params(ulong qbase1, ulong qbase2, uint32_t nframes,
483 			    uint32_t frame_size)
484 {
485 	int ret = 0;
486 
487 	BUG_ON(offsetof(struct tegra_ivc_channel_header, w_count) &
488 	       (TEGRA_IVC_ALIGN - 1));
489 	BUG_ON(offsetof(struct tegra_ivc_channel_header, r_count) &
490 	       (TEGRA_IVC_ALIGN - 1));
491 	BUG_ON(sizeof(struct tegra_ivc_channel_header) &
492 	       (TEGRA_IVC_ALIGN - 1));
493 
494 	if ((uint64_t)nframes * (uint64_t)frame_size >= 0x100000000) {
495 		pr_err("tegra_ivc: nframes * frame_size overflows\n");
496 		return -EINVAL;
497 	}
498 
499 	/*
500 	 * The headers must at least be aligned enough for counters
501 	 * to be accessed atomically.
502 	 */
503 	if ((qbase1 & (TEGRA_IVC_ALIGN - 1)) ||
504 	    (qbase2 & (TEGRA_IVC_ALIGN - 1))) {
505 		pr_err("tegra_ivc: channel start not aligned\n");
506 		return -EINVAL;
507 	}
508 
509 	if (frame_size & (TEGRA_IVC_ALIGN - 1)) {
510 		pr_err("tegra_ivc: frame size not adequately aligned\n");
511 		return -EINVAL;
512 	}
513 
514 	if (qbase1 < qbase2) {
515 		if (qbase1 + frame_size * nframes > qbase2)
516 			ret = -EINVAL;
517 	} else {
518 		if (qbase2 + frame_size * nframes > qbase1)
519 			ret = -EINVAL;
520 	}
521 
522 	if (ret) {
523 		pr_err("tegra_ivc: queue regions overlap\n");
524 		return ret;
525 	}
526 
527 	return 0;
528 }
529 
tegra_ivc_init(struct tegra_ivc * ivc,ulong rx_base,ulong tx_base,uint32_t nframes,uint32_t frame_size,void (* notify)(struct tegra_ivc *))530 int tegra_ivc_init(struct tegra_ivc *ivc, ulong rx_base, ulong tx_base,
531 		   uint32_t nframes, uint32_t frame_size,
532 		   void (*notify)(struct tegra_ivc *))
533 {
534 	int ret;
535 
536 	if (!ivc)
537 		return -EINVAL;
538 
539 	ret = check_ivc_params(rx_base, tx_base, nframes, frame_size);
540 	if (ret)
541 		return ret;
542 
543 	ivc->rx_channel = (struct tegra_ivc_channel_header *)rx_base;
544 	ivc->tx_channel = (struct tegra_ivc_channel_header *)tx_base;
545 	ivc->w_pos = 0;
546 	ivc->r_pos = 0;
547 	ivc->nframes = nframes;
548 	ivc->frame_size = frame_size;
549 	ivc->notify = notify;
550 
551 	return 0;
552 }
553