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