1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2023 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 #define NVOC_CRASHCAT_QUEUE_H_PRIVATE_ACCESS_ALLOWED
25 #include "crashcat/crashcat_queue.h"
26 #include "crashcat/crashcat_engine.h"
27 #include "crashcat/crashcat_report.h"
28 #include "utils/nvassert.h"
29 #include "nv-crashcat-decoder.h"
30
_getCrashCatReportHalspecArgs(void * pBuf,NvU8 * pFormatVersion,NvU64 * pImplementerSig)31 static NV_STATUS _getCrashCatReportHalspecArgs
32 (
33 void *pBuf,
34 NvU8 *pFormatVersion,
35 NvU64 *pImplementerSig
36 )
37 {
38 NvCrashCatPacketHeader_V1 header = *(NvCrashCatPacketHeader_V1 *)pBuf;
39
40 // Verify the report data looks sane
41 NV_CHECK_OR_RETURN(LEVEL_ERROR, crashcatPacketHeaderValid(header), NV_ERR_INVALID_DATA);
42
43 *pFormatVersion = crashcatPacketHeaderFormatVersion(header);
44 switch (*pFormatVersion)
45 {
46 default:
47 // Fall through for backward compatibility if version is not recognized
48 case NV_CRASHCAT_PACKET_FORMAT_VERSION_1:
49 {
50 NvCrashCatReport_V1 *pReport = (NvCrashCatReport_V1 *)pBuf;
51 *pImplementerSig = pReport->implementerSignature;
52 break;
53 }
54 }
55
56 return NV_OK;
57 }
58
crashcatQueueConsumeNextReport_V1(CrashCatQueue * pQueue)59 CrashCatReport *crashcatQueueConsumeNextReport_V1(CrashCatQueue *pQueue)
60 {
61 const NvU32 size = pQueue->config.size;
62 const NvU32 put = crashcatEnginePriRead(pQueue->pEngine, pQueue->config.putRegOffset);
63 NvU32 get = crashcatEnginePriRead(pQueue->pEngine, pQueue->config.getRegOffset);
64
65 NV_CHECK_OR_RETURN(LEVEL_ERROR, get < size, NULL);
66 NV_CHECK_OR_RETURN(LEVEL_ERROR, put < size, NULL);
67 NV_CHECK_OR_RETURN(LEVEL_SILENT, put != get, NULL); // Nothing to read
68
69 NvU32 readSize;
70 void *pBuf;
71 NvBool bAllocated = NV_FALSE;
72
73 if (put > get)
74 {
75 // We can read directly from the mapping
76 readSize = put - get;
77 crashcatEngineSyncCrashBuffer(pQueue->pEngine, pQueue->pMapping, get, readSize);
78 pBuf = (void *)((NvUPtr)pQueue->pMapping + get);
79 }
80 else if (put == 0)
81 {
82 // Buffer just wrapped, but we can still read directly from the mapping
83 readSize = size - get;
84 crashcatEngineSyncCrashBuffer(pQueue->pEngine, pQueue->pMapping, get, readSize);
85 pBuf = (void *)((NvUPtr)pQueue->pMapping + get);
86 }
87 else
88 {
89 // Need to handle wraparound, allocate a temporary buffer to simplify decoding
90 NvU32 preWrapSize = size - get;
91 NvU32 postWrapSize = put;
92 readSize = preWrapSize + postWrapSize;
93 pBuf = portMemAllocNonPaged(readSize);
94 NV_CHECK_OR_RETURN(LEVEL_ERROR, pBuf != NULL, NULL);
95 bAllocated = NV_TRUE;
96
97 crashcatEngineSyncCrashBuffer(pQueue->pEngine, pQueue->pMapping, get, preWrapSize);
98 portMemCopy(pBuf, preWrapSize,
99 (void *)((NvUPtr)pQueue->pMapping + get), preWrapSize);
100
101 crashcatEngineSyncCrashBuffer(pQueue->pEngine, pQueue->pMapping, 0, postWrapSize);
102 portMemCopy((void *)((NvUPtr)pBuf + preWrapSize), postWrapSize,
103 pQueue->pMapping, postWrapSize);
104 }
105
106 //
107 // To create the CrashCatReport object, we pass the implementer signature as a halspec arg.
108 // The implementer signature location is technically report-version-specific, so we need a
109 // little adapter logic to get the right one before the report is created.
110 //
111 CrashCatReport *pReport = NULL;
112 void *pReadBuf = pBuf;
113 NV_STATUS status = NV_ERR_INVALID_DATA;
114 NvU8 reportFormatVersion;
115 NvU64 reportImplementer;
116
117 NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR,
118 _getCrashCatReportHalspecArgs(pBuf, &reportFormatVersion, &reportImplementer),
119 updateGetPointer);
120
121 NV_CHECK_OK(status, LEVEL_ERROR,
122 objCreate(&pReport, pQueue, CrashCatReport,
123 reportFormatVersion, reportImplementer,
124 &pReadBuf, readSize));
125
126 updateGetPointer:
127 //
128 // Update the get pointer based on how many bytes were read, or skip it all if there was an
129 // extraction failure, so that we don't flood the logs with repeated failed extraction attempts.
130 // TODO: log raw data somewhere so the failure can be analyzed.
131 //
132 {
133 NvU64 diff = (NvU64)pReadBuf - (NvU64)pBuf;
134 if ((status != NV_OK) || (diff == 0))
135 diff = readSize;
136
137 NV_ASSERT_CHECKED(NvU64_HI32(diff) == 0);
138 get = (get + NvU64_LO32(diff)) % size;
139 crashcatEnginePriWrite(pQueue->pEngine, pQueue->config.getRegOffset, get);
140 }
141
142 if (bAllocated)
143 portMemFree(pBuf);
144
145 return pReport;
146 }
147