1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 1993-2021 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 * SPDX-License-Identifier: MIT
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 /******************************* DisplayPort********************************\
25 * *
26 * Module: dp_merger.cpp *
27 * Asynchronous Message merger *
28 * *
29 \***************************************************************************/
30
31 #include "dp_internal.h"
32 #include "dp_bitstream.h"
33 #include "dp_merger.h"
34 #include "dp_auxdefs.h"
35 #include "dp_crc.h"
36 #include "dp_messageheader.h"
37
38 using namespace DisplayPort;
39
40
pushTransaction(MessageHeader * header,Buffer * data)41 EncodedMessage * MessageTransactionMerger::pushTransaction(MessageHeader * header, Buffer * data)
42 {
43 if (freeOnNextCall)
44 {
45 delete freeOnNextCall;
46 freeOnNextCall = 0;
47 }
48
49 IncompleteMessage * imsg = getTransactionRecord(header->address, header->messageNumber);
50
51 if (!imsg)
52 {
53 DP_LOG(("DP-MM> Ignore message due to OOM"));
54 return 0;
55 }
56
57 if (header->isTransactionStart)
58 {
59 imsg->message.isPathMessage = header->isPathMessage;
60 imsg->message.isBroadcast = header->isBroadcast;
61 }
62 else
63 {
64 if (imsg->message.buffer.length == 0)
65 {
66 DP_LOG(("DP-MM> Expected transaction-start, ignoring message transaction"));
67 return 0;
68 }
69
70 if (imsg->message.isPathMessage != header->isPathMessage ||
71 imsg->message.isBroadcast != header->isBroadcast)
72 {
73 DP_ASSERT(0 && "Message type changed during transmission");
74 }
75 }
76
77 //
78 // Check for redundant start
79 //
80 if (header->isTransactionStart && imsg->message.buffer.length)
81 {
82 DP_LOG(("DP-MM> Unexpected repeated transaction-start, resetting message state."));
83
84 // We must have seen a previous incomplete transaction from this device
85 // they've begun a new packet. Forget about the old thing
86 imsg->message.buffer.reset();
87 }
88
89 //
90 // Kill the buffer if we've got less payload than we should
91 //
92 if (header->payloadBytes > data->length)
93 {
94 freeOnNextCall = imsg;
95 imsg->message.buffer.reset();
96 DP_LOG(("DP-MM> Received truncated or corrupted message transaction"));
97 return 0;
98 }
99
100 //
101 // Verify transaction CRC
102 //
103 BitStreamReader bsr(data, header->headerSizeBits, (header->payloadBytes-1)*8);
104 NvU8 dataCrc = (NvU8)dpCalculateBodyCRC(&bsr);
105
106 DP_ASSERT(header->headerSizeBits % 8 == 0 && "Header must be byte aligned");
107
108 if (dataCrc != data->data[header->headerSizeBits/8 + header->payloadBytes - 1] ||
109 header->payloadBytes == 0)
110 {
111 DP_LOG(("DP-MM> Received corruption message transactions"));
112 freeOnNextCall = imsg;
113 imsg->message.buffer.reset();
114 return 0;
115 }
116
117 // Discount the processed CRC from the payload count
118 header->payloadBytes--;
119
120 //
121 // Append active buffer
122 //
123 unsigned i = imsg->message.buffer.length;
124 imsg->message.buffer.resize(i + header->payloadBytes);
125 dpMemCopy(&imsg->message.buffer.data[i], &data->data[header->headerSizeBits/8], header->payloadBytes);
126
127 //
128 // Check for end of message transaction
129 //
130 if (header->isTransactionEnd)
131 {
132 freeOnNextCall = imsg;
133
134 return &imsg->message;
135 }
136
137 return 0;
138 }
139
getTransactionRecord(const Address & address,unsigned messageNumber)140 MessageTransactionMerger::IncompleteMessage * MessageTransactionMerger::getTransactionRecord(const Address & address, unsigned messageNumber)
141 {
142 IncompleteMessage * msg;
143 NvU64 currentTime = this->timer->getTimeUs();
144
145 //
146 // Search for existing record
147 //
148 for (ListElement * i = incompleteMessages.begin();i != incompleteMessages.end();)
149 {
150 msg = (IncompleteMessage *)i;
151 i = i->next;
152 if (msg->message.address == address && msg->message.messageNumber == messageNumber)
153 {
154 goto found;
155 }
156
157 //
158 // Found a stale message in the list
159 //
160 if (msg->lastUpdated + incompleteMessageTimeoutMs < currentTime)
161 delete msg;
162 }
163
164 //
165 // None exists? Add a new one
166 //
167 msg = new IncompleteMessage();
168 msg->message.address = address;
169 msg->message.messageNumber = messageNumber;
170 this->incompleteMessages.insertFront(msg);
171
172 found:
173 //
174 // Update the timestamp
175 //
176 msg->lastUpdated = currentTime;
177
178 return msg;
179 }
180
mailboxInterrupt()181 void IncomingTransactionManager::mailboxInterrupt()
182 {
183 MessageHeader msg;
184 unsigned totalSize;
185 AuxRetry::status result;
186 unsigned txSize = (unsigned)getTransactionSize();
187
188 //
189 // Size the static aux window
190 //
191 this->localWindow.resize(DP_MAX((unsigned)getTransactionSize(), (unsigned)getMessageBoxSize()));
192 if (this->localWindow.isError())
193 return;
194
195 //
196 // Read one aux-transaction worth of data
197 //
198 result = readMessageBox(0, &this->localWindow.data[0], txSize);
199
200 DP_ASSERT( result != AuxRetry::defer && "Unexpected?!" );
201
202 if (result != AuxRetry::ack)
203 return;
204
205 BitStreamReader reader(&this->localWindow, 0, 8*txSize);
206
207
208 //
209 // Before decoding the header, start with the downstream
210 // ports address prefix
211 //
212 if (!decodeHeader(&reader, &msg, addressPrefix))
213 {
214 //
215 // It's possible we should be NACKing here. Ignoring for now
216 // to allow the message originator to time out (can take seconds).
217 //
218 DP_ASSERT(0 && "Not yet implemented");
219
220 return;
221 }
222
223 //
224 // Let's get the entire sideband message in the localWindow
225 //
226
227 totalSize = (msg.headerSizeBits / 8) + msg.payloadBytes;
228
229 if (totalSize > txSize)
230 {
231 if (totalSize > DPCD_MESSAGEBOX_SIZE)
232 {
233 //
234 // Corrupt packet - total packet can't be larger than the window
235 //
236 return;
237 }
238 if (AuxRetry::ack!=readMessageBox(txSize, &this->localWindow.data[txSize], totalSize - txSize))
239 {
240 //
241 // Failed to read second half of message
242 //
243 return;
244 }
245 }
246
247 clearMessageBoxInterrupt();
248
249 EncodedMessage * em = incompleteMessages.pushTransaction(&msg, &this->localWindow);
250
251 if (em)
252 {
253 this->sink->messagedReceived(this, em);
254 }
255 }
256
~IncomingTransactionManager()257 IncomingTransactionManager::~IncomingTransactionManager()
258 {
259 }
260
261
IncomingTransactionManager(Timer * timer,const Address & addressPrefix,IncomingTransactionManagerEventSink * sink)262 IncomingTransactionManager::IncomingTransactionManager(Timer * timer, const Address & addressPrefix, IncomingTransactionManagerEventSink * sink)
263 : incompleteMessages(timer, DP_INCOMPLETE_MESSAGE_TIMEOUT_USEC), addressPrefix(addressPrefix)
264 {
265 this->sink = sink;
266 this->timer = timer;
267 }
268
269
270
readMessageBox(NvU32 offset,NvU8 * data,size_t length)271 AuxRetry::status DownReplyManager::readMessageBox(NvU32 offset, NvU8 * data, size_t length)
272 {
273 return hal->readDownReplyMessageBox(offset, data, length);
274 }
275
getMessageBoxSize()276 size_t DownReplyManager::getMessageBoxSize()
277 {
278 return hal->getDownReplyMessageBoxSize();
279 }
280
getTransactionSize()281 size_t DownReplyManager::getTransactionSize()
282 {
283 return hal->getTransactionSize();
284 }
285
clearMessageBoxInterrupt()286 void DownReplyManager::clearMessageBoxInterrupt()
287 {
288 hal->clearInterruptDownReplyReady();
289 }
290
readMessageBox(NvU32 offset,NvU8 * data,size_t length)291 AuxRetry::status UpRequestManager::readMessageBox(NvU32 offset, NvU8 * data, size_t length)
292 {
293 return hal->readUpRequestMessageBox(offset, data, length);
294 }
295
getMessageBoxSize()296 size_t UpRequestManager::getMessageBoxSize()
297 {
298 return hal->getUpRequestMessageBoxSize();
299 }
300
getTransactionSize()301 size_t UpRequestManager::getTransactionSize()
302 {
303 return hal->getTransactionSize();
304 }
305
clearMessageBoxInterrupt()306 void UpRequestManager::clearMessageBoxInterrupt()
307 {
308 hal->clearInterruptUpRequestReady();
309 }
310
311