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_ENGINE_H_PRIVATE_ACCESS_ALLOWED
25 #include "crashcat/crashcat_engine.h"
26 #include "crashcat/crashcat_queue.h"
27 #include "crashcat/crashcat_wayfinder.h"
28 #include "utils/nvassert.h"
29 #include "nv-crashcat-decoder.h"
30 
31 static NV_INLINE NvU64 _crashcatEngineComputeDescriptorKey(NV_CRASHCAT_MEM_APERTURE, NvU64);
32 static CrashCatBufferDescriptor *_crashcatEngineCreateBufferDescriptor(CrashCatEngine *,
33                                                                        NV_CRASHCAT_MEM_APERTURE,
34                                                                        NvU64, NvU64, void *);
35 static NV_INLINE void _crashcatEngineDestroyBufferDescriptor(CrashCatEngine *,
36                                                              CrashCatBufferDescriptor *);
37 static void *_crashcatEngineMapBufferDescriptor(CrashCatEngine *, CrashCatBufferDescriptor *);
38 static void _crashcatEngineUnmapBufferDescriptor(CrashCatEngine *, CrashCatBufferDescriptor *);
39 
crashcatEngineLoadWayfinder_IMPL(CrashCatEngine * pCrashCatEng)40 NV_STATUS crashcatEngineLoadWayfinder_IMPL(CrashCatEngine *pCrashCatEng)
41 {
42     NvU32 wfl0Offset = crashcatEngineGetWFL0Offset(pCrashCatEng);
43     NvU32 wfl0 = crashcatEnginePriRead(pCrashCatEng, wfl0Offset);
44 
45     // Has the L0 wayfinder been populated yet?
46     if (!crashcatWayfinderL0Valid(wfl0))
47         return NV_WARN_NOTHING_TO_DO;
48 
49     NV_CHECK_OK_OR_RETURN(LEVEL_ERROR,
50         objCreate(&pCrashCatEng->pWayfinder, pCrashCatEng, CrashCatWayfinder,
51                   crashcatWayfinderL0Version(wfl0), // halspec args
52                   wfl0));                           // constructor args
53 
54     return NV_OK;
55 }
56 
crashcatEngineGetNextCrashReport_IMPL(CrashCatEngine * pCrashCatEng)57 CrashCatReport *crashcatEngineGetNextCrashReport_IMPL(CrashCatEngine *pCrashCatEng)
58 {
59     // Don't attempt to probe for reports if CrashCat is not configured
60     if (!crashcatEngineConfigured(pCrashCatEng))
61         return NULL;
62 
63     // No reports if there's no wayfinder yet
64     if ((pCrashCatEng->pWayfinder == NULL) &&
65         (crashcatEngineLoadWayfinder(pCrashCatEng) != NV_OK))
66         return NULL;
67 
68     CrashCatQueue *pQueue = crashcatWayfinderGetReportQueue_HAL(pCrashCatEng->pWayfinder);
69     if (pQueue != NULL)
70         return crashcatQueueConsumeNextReport_HAL(pQueue);
71 
72     return NULL;
73 }
74 
crashcatEngineConstruct_IMPL(CrashCatEngine * pCrashCatEng)75 NV_STATUS crashcatEngineConstruct_IMPL
76 (
77     CrashCatEngine *pCrashCatEng
78 )
79 {
80     mapInitIntrusive(&pCrashCatEng->registeredCrashBuffers);
81     mapInitIntrusive(&pCrashCatEng->mappedCrashBuffers);
82 
83     return NV_OK;
84 }
85 
crashcatEngineDestruct_IMPL(CrashCatEngine * pCrashCatEng)86 void crashcatEngineDestruct_IMPL
87 (
88     CrashCatEngine *pCrashCatEng
89 )
90 {
91     crashcatEngineUnload(pCrashCatEng);
92 
93     // All buffers should be unmapped and unregistered before the destructor is called
94     NV_ASSERT(mapCount(&pCrashCatEng->mappedCrashBuffers) == 0);
95     NV_ASSERT(mapCount(&pCrashCatEng->registeredCrashBuffers) == 0);
96 
97     mapDestroy(&pCrashCatEng->mappedCrashBuffers);
98     mapDestroy(&pCrashCatEng->registeredCrashBuffers);
99 }
100 
crashcatEngineUnload_IMPL(CrashCatEngine * pCrashCatEng)101 void crashcatEngineUnload_IMPL
102 (
103     CrashCatEngine *pCrashCatEng
104 )
105 {
106     objDelete(pCrashCatEng->pWayfinder);
107 }
108 
109 // Non-NVOC wrapper to handle variadic arguments
crashcatEnginePrintf(CrashCatEngine * pCrashCatEng,NvBool bReportStart,const char * fmt,...)110 void crashcatEnginePrintf(CrashCatEngine *pCrashCatEng, NvBool bReportStart, const char *fmt, ...)
111 {
112     va_list args;
113     va_start(args, fmt);
114     // Dispatches virtual function with va_list
115     crashcatEngineVprintf(pCrashCatEng, bReportStart, fmt, args);
116     va_end(args);
117 }
118 
_crashcatEngineComputeDescriptorKey(NV_CRASHCAT_MEM_APERTURE aperture,NvU64 offset)119 static NV_INLINE NvU64 _crashcatEngineComputeDescriptorKey
120 (
121     NV_CRASHCAT_MEM_APERTURE aperture,
122     NvU64 offset
123 )
124 {
125     // Offset should be at least 8-byte aligned so that the aperture bits can be stuffed
126     NV_ASSERT_CHECKED((offset & (sizeof(NvU64) - 1)) == 0);
127     return (offset | (NvU64)aperture);
128 }
129 
_crashcatEngineCreateBufferDescriptor(CrashCatEngine * pCrashCatEng,NV_CRASHCAT_MEM_APERTURE aperture,NvU64 offset,NvU64 size,void * pEngPriv)130 static CrashCatBufferDescriptor *_crashcatEngineCreateBufferDescriptor
131 (
132     CrashCatEngine *pCrashCatEng,
133     NV_CRASHCAT_MEM_APERTURE aperture,
134     NvU64 offset,
135     NvU64 size,
136     void *pEngPriv
137 )
138 {
139     CrashCatBufferDescriptor *pBufDesc = portMemAllocNonPaged(sizeof(*pBufDesc));
140     if (pBufDesc == NULL)
141         return NULL;
142 
143     portMemSet(pBufDesc, 0, sizeof(*pBufDesc));
144 
145     pBufDesc->bRegistered = NV_FALSE;
146     pBufDesc->aperture = aperture;
147     pBufDesc->physOffset = offset;
148     pBufDesc->size = size;
149     pBufDesc->pEngPriv = pEngPriv;
150 
151     return pBufDesc;
152 }
153 
_crashcatEngineDestroyBufferDescriptor(CrashCatEngine * pCrashCatEng,CrashCatBufferDescriptor * pBufDesc)154 static NV_INLINE void _crashcatEngineDestroyBufferDescriptor
155 (
156     CrashCatEngine *pCrashCatEng,
157     CrashCatBufferDescriptor *pBufDesc
158 )
159 {
160     portMemFree(pBufDesc);
161 }
162 
_crashcatEngineMapBufferDescriptor(CrashCatEngine * pCrashCatEng,CrashCatBufferDescriptor * pBufDesc)163 static void *_crashcatEngineMapBufferDescriptor
164 (
165     CrashCatEngine *pCrashCatEng,
166     CrashCatBufferDescriptor *pBufDesc
167 )
168 {
169     void *ptr = NULL;
170 
171     switch (pBufDesc->aperture)
172     {
173         case NV_CRASHCAT_MEM_APERTURE_SYSGPA:
174         case NV_CRASHCAT_MEM_APERTURE_FBGPA:
175         {
176             ptr = crashcatEngineMapBufferDescriptor(pCrashCatEng, pBufDesc);
177             NV_CHECK_OR_RETURN(LEVEL_ERROR, ptr != NULL, NULL);
178             break;
179         }
180         case NV_CRASHCAT_MEM_APERTURE_DMEM:
181         case NV_CRASHCAT_MEM_APERTURE_EMEM:
182         {
183             ptr = portMemAllocNonPaged(pBufDesc->size);
184             NV_CHECK_OR_RETURN(LEVEL_ERROR, ptr != NULL, NULL);
185             break;
186         }
187         default:
188             NV_PRINTF(LEVEL_WARNING,
189                 "Unknown CrashCat aperture ID 0x%02x (offset = 0x%" NvU64_fmtx
190                 ", size = 0x%" NvU64_fmtx ")\n",
191                 pBufDesc->aperture, pBufDesc->physOffset, pBufDesc->size);
192             break;
193     }
194 
195     return ptr;
196 }
197 
_crashcatEngineUnmapBufferDescriptor(CrashCatEngine * pCrashCatEng,CrashCatBufferDescriptor * pBufDesc)198 static void _crashcatEngineUnmapBufferDescriptor
199 (
200     CrashCatEngine *pCrashCatEng,
201     CrashCatBufferDescriptor *pBufDesc
202 )
203 {
204     switch (pBufDesc->aperture)
205     {
206         case NV_CRASHCAT_MEM_APERTURE_SYSGPA:
207         case NV_CRASHCAT_MEM_APERTURE_FBGPA:
208             crashcatEngineUnmapBufferDescriptor(pCrashCatEng, pBufDesc);
209             break;
210         case NV_CRASHCAT_MEM_APERTURE_DMEM:
211         case NV_CRASHCAT_MEM_APERTURE_EMEM:
212             portMemFree(pBufDesc->pMapping);
213             break;
214         default:
215             NV_PRINTF(LEVEL_WARNING,
216                 "Unknown CrashCat aperture ID 0x%02x (offset = 0x%" NvU64_fmtx
217                 ", size = 0x%" NvU64_fmtx ")\n",
218                 pBufDesc->aperture, pBufDesc->physOffset, pBufDesc->size);
219             break;
220     }
221 }
222 
crashcatEngineRegisterCrashBuffer_IMPL(CrashCatEngine * pCrashCatEng,NV_CRASHCAT_MEM_APERTURE aperture,NvU64 offset,NvU64 size,void * pEngPriv)223 NV_STATUS crashcatEngineRegisterCrashBuffer_IMPL
224 (
225     CrashCatEngine *pCrashCatEng,
226     NV_CRASHCAT_MEM_APERTURE aperture,
227     NvU64 offset,
228     NvU64 size,
229     void *pEngPriv
230 )
231 {
232     NV_CHECK_OR_RETURN(LEVEL_INFO, (aperture == NV_CRASHCAT_MEM_APERTURE_SYSGPA) ||
233                                    (aperture == NV_CRASHCAT_MEM_APERTURE_FBGPA),
234                        NV_ERR_INVALID_ARGUMENT);
235     NV_CHECK_OR_RETURN(LEVEL_INFO, size > 0, NV_ERR_INVALID_ARGUMENT);
236     NV_CHECK_OR_RETURN(LEVEL_INFO, pEngPriv != NULL, NV_ERR_INVALID_ARGUMENT);
237 
238     // Create a crashcat buffer descriptor and register in the registeredCrashBuffers
239     CrashCatBufferDescriptor *pBufDesc = _crashcatEngineCreateBufferDescriptor(pCrashCatEng,
240                                                                                aperture,
241                                                                                offset, size,
242                                                                                pEngPriv);
243     if (pBufDesc == NULL)
244         return NV_ERR_NO_MEMORY;
245 
246     pBufDesc->bRegistered = NV_TRUE;
247 
248     NvU64 key = _crashcatEngineComputeDescriptorKey(aperture, offset);
249     if (!mapInsertExisting(&pCrashCatEng->registeredCrashBuffers, key, pBufDesc))
250     {
251         _crashcatEngineDestroyBufferDescriptor(pCrashCatEng, pBufDesc);
252         return NV_ERR_INSERT_DUPLICATE_NAME;
253     }
254 
255     return NV_OK;
256 }
257 
crashcatEngineUnregisterCrashBuffer_IMPL(CrashCatEngine * pCrashCatEng,NV_CRASHCAT_MEM_APERTURE aperture,NvU64 offset,NvU64 size)258 void crashcatEngineUnregisterCrashBuffer_IMPL
259 (
260     CrashCatEngine *pCrashCatEng,
261     NV_CRASHCAT_MEM_APERTURE aperture,
262     NvU64 offset,
263     NvU64 size
264 )
265 {
266     NvU64 key = _crashcatEngineComputeDescriptorKey(aperture, offset);
267     CrashCatBufferDescriptor *pBufDesc = mapFind(&pCrashCatEng->registeredCrashBuffers, key);
268     if (pBufDesc == NULL)
269         return;
270 
271     NV_ASSERT_CHECKED(pBufDesc->size == size);
272 
273     //
274     // CrashCat should be unloaded from the engine before unregistering the crash buffer.
275     // Unload will unmap all buffers.
276     //
277     NV_ASSERT_CHECKED(pBufDesc->pMapping == NULL);
278 
279     mapRemove(&pCrashCatEng->registeredCrashBuffers, pBufDesc);
280     _crashcatEngineDestroyBufferDescriptor(pCrashCatEng, pBufDesc);
281 }
282 
crashcatEngineMapCrashBuffer_IMPL(CrashCatEngine * pCrashCatEng,NV_CRASHCAT_MEM_APERTURE aperture,NvU64 offset,NvU64 size)283 void *crashcatEngineMapCrashBuffer_IMPL
284 (
285     CrashCatEngine *pCrashCatEng,
286     NV_CRASHCAT_MEM_APERTURE aperture,
287     NvU64 offset,
288     NvU64 size
289 )
290 {
291     NvU64 key = _crashcatEngineComputeDescriptorKey(aperture, offset);
292     CrashCatBufferDescriptor *pBufDesc = mapFind(&pCrashCatEng->registeredCrashBuffers, key);
293 
294     // Sysmem buffers always need to be pre-registered
295     if ((aperture != NV_CRASHCAT_MEM_APERTURE_SYSGPA) && (pBufDesc == NULL))
296         pBufDesc = _crashcatEngineCreateBufferDescriptor(pCrashCatEng, aperture,
297                                                          offset, size, NULL);
298 
299     NV_CHECK_OR_RETURN(LEVEL_ERROR, pBufDesc != NULL, NULL);
300     NV_ASSERT_CHECKED(pBufDesc->size == size);
301 
302     pBufDesc->pMapping = _crashcatEngineMapBufferDescriptor(pCrashCatEng, pBufDesc);
303 
304     if ((pBufDesc->pMapping == NULL) ||
305         !mapInsertExisting(&pCrashCatEng->mappedCrashBuffers, (NvU64)pBufDesc->pMapping, pBufDesc))
306     {
307         if (pBufDesc->pMapping != NULL)
308         {
309             _crashcatEngineUnmapBufferDescriptor(pCrashCatEng, pBufDesc);
310             pBufDesc->pMapping = NULL;
311         }
312 
313         //
314         // If this wasn't a registered buffer descriptor, it was created above, so destroy it
315         // before returning.
316         //
317         if (!pBufDesc->bRegistered)
318             _crashcatEngineDestroyBufferDescriptor(pCrashCatEng, pBufDesc);
319 
320         return NULL;
321     }
322 
323     return pBufDesc->pMapping;
324 }
325 
crashcatEngineUnmapCrashBuffer_IMPL(CrashCatEngine * pCrashCatEng,void * ptr)326 void crashcatEngineUnmapCrashBuffer_IMPL
327 (
328     CrashCatEngine *pCrashCatEng,
329     void *ptr
330 )
331 {
332     CrashCatBufferDescriptor *pBufDesc = mapFind(&pCrashCatEng->mappedCrashBuffers, (NvU64)ptr);
333     if (pBufDesc == NULL)
334         return;
335 
336     NV_ASSERT_CHECKED(ptr == pBufDesc->pMapping);
337 
338     mapRemove(&pCrashCatEng->mappedCrashBuffers, pBufDesc);
339     _crashcatEngineUnmapBufferDescriptor(pCrashCatEng, pBufDesc);
340     pBufDesc->pMapping = NULL;
341 
342     // If this was not a registered buffer, destroy the buffered descriptor now
343     if (!pBufDesc->bRegistered)
344         _crashcatEngineDestroyBufferDescriptor(pCrashCatEng, pBufDesc);
345 }
346 
crashcatEngineSyncCrashBuffer_IMPL(CrashCatEngine * pCrashCatEng,void * ptr,NvU32 offset,NvU32 size)347 void crashcatEngineSyncCrashBuffer_IMPL
348 (
349     CrashCatEngine *pCrashCatEng,
350     void *ptr,
351     NvU32 offset,
352     NvU32 size
353 )
354 {
355     CrashCatBufferDescriptor *pBufDesc = mapFind(&pCrashCatEng->mappedCrashBuffers, (NvU64)ptr);
356 
357     NV_ASSERT_OR_RETURN_VOID(pBufDesc != NULL);
358 
359     // Direct-map buffers don't require any sync
360     if ((pBufDesc->aperture == NV_CRASHCAT_MEM_APERTURE_SYSGPA) ||
361         (pBufDesc->aperture == NV_CRASHCAT_MEM_APERTURE_FBGPA))
362         return;
363 
364     crashcatEngineSyncBufferDescriptor(pCrashCatEng, pBufDesc, offset, size);
365 }
366