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_KERNEL_CRASHCAT_ENGINE_H_PRIVATE_ACCESS_ALLOWED
25 #include "gpu/falcon/kernel_crashcat_engine.h"
26 #include "gpu/gpu.h"
27 #include "core/printf.h"
28 #include "os/nv_memory_type.h"
29 
30 NV_STATUS kcrashcatEngineConfigure_IMPL
31 (
32     KernelCrashCatEngine *pKernelCrashCatEng,
33     KernelCrashCatEngineConfig *pEngConfig
34 )
35 {
36     if (!pEngConfig->bEnable)
37         return NV_OK;
38 
39     NV_CHECK_OR_RETURN(LEVEL_ERROR, pEngConfig->pName != NULL, NV_ERR_INVALID_ARGUMENT);
40     NV_CHECK_OR_RETURN(LEVEL_ERROR, pEngConfig->errorId != 0,  NV_ERR_INVALID_ARGUMENT);
41 
42     pKernelCrashCatEng->bConfigured = NV_TRUE;
43     pKernelCrashCatEng->pName = pEngConfig->pName;
44     pKernelCrashCatEng->errorId = pEngConfig->errorId;
45     pKernelCrashCatEng->pGpu = ENG_GET_GPU(pKernelCrashCatEng);
46     pKernelCrashCatEng->dmemPort = pEngConfig->dmemPort;
47 
48     if (pEngConfig->allocQueueSize > 0)
49     {
50         const NvU32 CRASHCAT_QUEUE_ALIGNMENT = 1u << 10;
51         pEngConfig->allocQueueSize = NV_ALIGN_UP(pEngConfig->allocQueueSize,
52                                                  CRASHCAT_QUEUE_ALIGNMENT);
53         NV_STATUS status;
54 
55         //
56         // The queue must be contiguous and 1KB aligned in both size and offset.
57         // Typically the queue will be a single page to satisfy these requirements.
58         //
59         NV_CHECK_OK_OR_RETURN(LEVEL_ERROR,
60             memdescCreate(&pKernelCrashCatEng->pQueueMemDesc, pKernelCrashCatEng->pGpu,
61                           pEngConfig->allocQueueSize, CRASHCAT_QUEUE_ALIGNMENT, NV_TRUE,
62                           ADDR_SYSMEM, NV_MEMORY_CACHED, MEMDESC_FLAGS_NONE));
63 
64         NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR,
65             memdescAlloc(pKernelCrashCatEng->pQueueMemDesc),
66             memdescCleanup);
67 
68         //
69         // After kcrashcatEngineRegisterCrashBuffer(), the CrashCat library should be able to map
70         // and access the queue buffer when it shows up in a wayfinder.
71         //
72         NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR,
73             kcrashcatEngineRegisterCrashBuffer(pKernelCrashCatEng,
74                                                pKernelCrashCatEng->pQueueMemDesc),
75             memdescCleanup);
76 
77 memdescCleanup:
78         if (status != NV_OK)
79         {
80             kcrashcatEngineUnload(pKernelCrashCatEng);
81             return status;
82         }
83     }
84 
85     return NV_OK;
86 }
87 
88 NvBool kcrashcatEngineConfigured_IMPL(KernelCrashCatEngine *pKernelCrashCatEng)
89 {
90     return pKernelCrashCatEng->bConfigured;
91 }
92 
93 MEMORY_DESCRIPTOR *kcrashcatEngineGetQueueMemDesc_IMPL(KernelCrashCatEngine *pKernelCrashCatEng)
94 {
95     return pKernelCrashCatEng->pQueueMemDesc;
96 }
97 
98 void kcrashcatEngineUnload_IMPL(KernelCrashCatEngine *pKernelCrashCatEng)
99 {
100     if (pKernelCrashCatEng->pQueueMemDesc != NULL)
101     {
102         kcrashcatEngineUnregisterCrashBuffer(pKernelCrashCatEng, pKernelCrashCatEng->pQueueMemDesc);
103         memdescFree(pKernelCrashCatEng->pQueueMemDesc);
104         memdescDestroy(pKernelCrashCatEng->pQueueMemDesc);
105         pKernelCrashCatEng->pQueueMemDesc = NULL;
106     }
107 
108     crashcatEngineUnload_IMPL(staticCast(pKernelCrashCatEng, CrashCatEngine));
109 }
110 
111 NvU32 kcrashcatEnginePriRead_IMPL
112 (
113     KernelCrashCatEngine *pKernelCrashCatEng,
114     NvU32 offset
115 )
116 {
117     return kcrashcatEngineRegRead(pKernelCrashCatEng->pGpu, pKernelCrashCatEng, offset);
118 }
119 
120 void kcrashcatEnginePriWrite_IMPL
121 (
122     KernelCrashCatEngine *pKernelCrashCatEng,
123     NvU32 offset,
124     NvU32 data
125 )
126 {
127     kcrashcatEngineRegWrite(pKernelCrashCatEng->pGpu, pKernelCrashCatEng, offset, data);
128 }
129 
130 void kcrashcatEngineVprintf_IMPL
131 (
132     KernelCrashCatEngine *pKernelCrashCatEng,
133     NvBool bReportStart,
134     const char *fmt,
135     va_list args
136 )
137 {
138     //
139     // The first line logs an Xid - subsequent crash report lines are printed via
140     // portDbgPrintString() so that they are in dmesg, but don't cause additional Xid "events".
141     //
142     if (bReportStart)
143     {
144         va_list argsCopy;
145 
146         //
147         // Prefix the engine name to the format string.
148         // nvErrorLog() appends a newline, so we don't add one here.
149         //
150         nvDbgSnprintf(pKernelCrashCatEng->fmtBuffer, MAX_ERROR_STRING, "%s %s",
151                       pKernelCrashCatEng->pName, fmt);
152 
153         va_copy(argsCopy, args);
154         nvErrorLog(pKernelCrashCatEng->pGpu, pKernelCrashCatEng->errorId,
155                    pKernelCrashCatEng->fmtBuffer, argsCopy);
156         va_end(argsCopy);
157     }
158 
159     // portDbgPrintString/NVLOG_PRINTF don't add a newline, so add one here
160     const char *newline = "\n";
161     const NvLength fmtSize = portStringLength(fmt) + 1;
162     const NvLength newlineSize = 3; // Two chars plus terminating null
163     const NvLength newFmtSize = fmtSize + newlineSize - 1; // terminating null is shared
164 
165     portMemCopy(pKernelCrashCatEng->fmtBuffer, MAX_ERROR_STRING, fmt, fmtSize);
166     portStringCat(pKernelCrashCatEng->fmtBuffer, newFmtSize, newline, newlineSize);
167     nvDbgVsnprintf(pKernelCrashCatEng->printBuffer, MAX_ERROR_STRING,
168                     pKernelCrashCatEng->fmtBuffer, args);
169 
170     // The report-starting line was already printed by nvErrorLog above
171     if (!bReportStart)
172         portDbgPrintString(pKernelCrashCatEng->printBuffer, MAX_ERROR_STRING);
173 
174     //
175     // Also print the formatted string to NvLog - avoid direct NV_PRINTF calls so as not to
176     // duplicate output in dmesg.
177     //
178     NVLOG_PRINTF(NV_PRINTF_MODULE, NVLOG_ROUTE_RM, LEVEL_ERROR, pKernelCrashCatEng->printBuffer);
179 }
180 
181 static NV_INLINE
182 NV_CRASHCAT_MEM_APERTURE _addressSpaceToCrashcatAperture(NV_ADDRESS_SPACE addrSpace)
183 {
184     switch (addrSpace)
185     {
186         case ADDR_SYSMEM: return NV_CRASHCAT_MEM_APERTURE_SYSGPA;
187         case ADDR_FBMEM:  return NV_CRASHCAT_MEM_APERTURE_FBGPA;
188         default: NV_ASSERT_OR_RETURN(0, NV_CRASHCAT_MEM_APERTURE_UNKNOWN);
189     }
190 }
191 
192 NV_STATUS kcrashcatEngineRegisterCrashBuffer_IMPL
193 (
194     KernelCrashCatEngine *pKernelCrashCatEng,
195     MEMORY_DESCRIPTOR *pMemDesc
196 )
197 {
198     return crashcatEngineRegisterCrashBuffer(staticCast(pKernelCrashCatEng, CrashCatEngine),
199         _addressSpaceToCrashcatAperture(memdescGetAddressSpace(pMemDesc)),
200         memdescGetPhysAddr(pMemDesc, AT_GPU, 0), memdescGetSize(pMemDesc),
201         pMemDesc);
202 }
203 
204 void kcrashcatEngineUnregisterCrashBuffer_IMPL
205 (
206     KernelCrashCatEngine *pKernelCrashCatEng,
207     MEMORY_DESCRIPTOR *pMemDesc
208 )
209 {
210     crashcatEngineUnregisterCrashBuffer(staticCast(pKernelCrashCatEng, CrashCatEngine),
211         _addressSpaceToCrashcatAperture(memdescGetAddressSpace(pMemDesc)),
212         memdescGetPhysAddr(pMemDesc, AT_GPU, 0), memdescGetSize(pMemDesc));
213 }
214 
215 static NV_INLINE NV_ADDRESS_SPACE _crashcatApertureToAddressSpace(NV_CRASHCAT_MEM_APERTURE aper)
216 {
217     switch (aper)
218     {
219         case NV_CRASHCAT_MEM_APERTURE_FBGPA: return ADDR_FBMEM;
220         case NV_CRASHCAT_MEM_APERTURE_SYSGPA: return ADDR_SYSMEM;
221         default: return ADDR_UNKNOWN;
222     }
223 }
224 
225 static MEMORY_DESCRIPTOR *_kcrashcatEngineCreateBufferMemDesc
226 (
227     KernelCrashCatEngine *pKernelCrashCatEng,
228     CrashCatBufferDescriptor *pBufDesc
229 )
230 {
231     // Convert the buffer descriptor to a set of memdesc parameters
232     MEMORY_DESCRIPTOR *pMemDesc;
233     NV_STATUS status;
234     NV_ADDRESS_SPACE bufAddrSpace = _crashcatApertureToAddressSpace(pBufDesc->aperture);
235     NV_CHECK_OK_OR_ELSE(status, LEVEL_ERROR,
236         memdescCreate(&pMemDesc, pKernelCrashCatEng->pGpu, pBufDesc->size, 0,
237                       NV_TRUE, bufAddrSpace, NV_MEMORY_CACHED, MEMDESC_FLAGS_NONE),
238         return NULL;);
239 
240     memdescDescribe(pMemDesc, bufAddrSpace, pBufDesc->physOffset, pBufDesc->size);
241     return pMemDesc;
242 }
243 
244 void *kcrashcatEngineMapBufferDescriptor_IMPL
245 (
246     KernelCrashCatEngine *pKernelCrashCatEng,
247     CrashCatBufferDescriptor *pBufDesc
248 )
249 {
250     MEMORY_DESCRIPTOR *pMemDesc;
251 
252     if (pBufDesc->bRegistered)
253         pMemDesc = pBufDesc->pEngPriv;
254     else
255         pMemDesc = _kcrashcatEngineCreateBufferMemDesc(pKernelCrashCatEng, pBufDesc);
256 
257     NV_CHECK_OR_RETURN(LEVEL_ERROR, pMemDesc != NULL, NULL);
258 
259     NvP64 pBuf, pPriv;
260     NV_STATUS status;
261 
262     // CrashCat buffers are read-only
263     NV_CHECK_OK_OR_ELSE(status, LEVEL_ERROR,
264         memdescMap(pMemDesc, 0, memdescGetSize(pMemDesc), NV_TRUE,
265                    NV_PROTECT_READABLE, &pBuf, &pPriv),
266         {
267             if (pBufDesc->pEngPriv == NULL)
268                 memdescDestroy(pMemDesc);
269             return NULL;
270         });
271 
272     memdescSetKernelMapping(pMemDesc, pBuf);
273     memdescSetKernelMappingPriv(pMemDesc, pPriv);
274     pBufDesc->pEngPriv = pMemDesc;
275     return NvP64_VALUE(pBuf);
276 }
277 
278 void kcrashcatEngineUnmapBufferDescriptor_IMPL
279 (
280     KernelCrashCatEngine *pKernelCrashCatEng,
281     CrashCatBufferDescriptor *pBufDesc
282 )
283 {
284     MEMORY_DESCRIPTOR *pMemDesc = pBufDesc->pEngPriv;
285     NvP64 pBuf = memdescGetKernelMapping(pMemDesc);
286     NvP64 pPriv = memdescGetKernelMappingPriv(pMemDesc);
287 
288     memdescUnmap(pMemDesc, NV_TRUE, 0, pBuf, pPriv);
289     memdescSetKernelMapping(pMemDesc, NULL);
290     memdescSetKernelMappingPriv(pMemDesc, NULL);
291 
292     if (!pBufDesc->bRegistered)
293         memdescDestroy(pMemDesc);
294 }
295 
296 void kcrashcatEngineSyncBufferDescriptor_IMPL
297 (
298     KernelCrashCatEngine *pKernelCrashCatEng,
299     CrashCatBufferDescriptor *pBufDesc,
300     NvU32 offset,
301     NvU32 size
302 )
303 {
304     //
305     // The buffers which support the "sync" operation don't have a memdesc - they are accessed
306     // through ports, so we copy the data out into a local buffer instead of direct map.
307     //
308     NV_ASSERT_CHECKED(NvU64_HI32(pBufDesc->physOffset) == 0);
309     NV_ASSERT_CHECKED(NvU64_HI32(pBufDesc->size) == 0);
310 
311     switch (pBufDesc->aperture)
312     {
313         case NV_CRASHCAT_MEM_APERTURE_DMEM:
314             kcrashcatEngineReadDmem_HAL(pKernelCrashCatEng,
315                                         NvU64_LO32(pBufDesc->physOffset) + offset,
316                                         size,
317                                         (void *)((NvUPtr)pBufDesc->pMapping + offset));
318             return;
319         case NV_CRASHCAT_MEM_APERTURE_EMEM:
320             kcrashcatEngineReadEmem_HAL(pKernelCrashCatEng,
321                                         NvU64_LO32(pBufDesc->physOffset) + offset,
322                                         size,
323                                         (void *)((NvUPtr)pBufDesc->pMapping + offset));
324             return;
325         default:
326             NV_ASSERT_CHECKED(0);
327     }
328 }
329