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