1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2020-2022 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 /**
25  * @file
26  * @brief UTIL module implementation implements helpter functions for
27  *
28  */
29 
30 #include "nvport/nvport.h"
31 #include "utils/nvassert.h"
32 
33 #if defined(NVRM) && !defined(NVWATCH)
34 #include "containers/map.h"
35 #include "os/os.h"
36 #include "nvrm_registry.h"
37 #include "rmconfig.h"
38 #include "gpu/gpu.h"
39 #include "gpu/gpu_user_shared_data.h"
40 #include "class/cl00de.h"
41 #elif !defined(RMCFG_FEATURE_ENABLED)
42 #define RMCFG_FEATURE_x 0
43 #endif
44 
45 #if NV_PRINTF_ENABLED || NV_JOURNAL_ASSERT_ENABLE
46 
47 // Hook NV_ASSERT into RCDB.
48 #if NV_JOURNAL_ASSERT_ENABLE
49 void rcdbRmAssert(NvU32 lineNum, NvU64 ip);
50 void rcdbRmAssertStatus(NvU32 status, NvU32 lineNum, NvU64 ip);
51 #define NV_JOURNAL_ASSERT_FAILURE(lineNum, ip)                  rcdbRmAssert(lineNum, (NvU64)(ip))
52 #define NV_JOURNAL_ASSERT_FAILURE_STATUS(lineNum, ip, status)   rcdbRmAssertStatus((status), lineNum, (NvU64)(ip))
53 #else
54 #define NV_JOURNAL_ASSERT_FAILURE(lineNum, ip)                  ((void)0)
55 #define NV_JOURNAL_ASSERT_FAILURE_STATUS(lineNum, ip, status)   ((void)0)
56 #endif /* NV_JOURNAL_ASSERT_ENABLE*/
57 
58 #if defined(GSP_PLUGIN_BUILD) || (defined(NVRM) && NVCPU_IS_RISCV64)
59 
60 #if NV_JOURNAL_ASSERT_ENABLE
61 static void
62 _logAssertCount(void)
63 {
64     static NvU32 assertCount = 0;
65     NV00DE_SHARED_DATA *pSharedData = gpushareddataWriteStart(g_pGpu);
66 
67     pSharedData->gspAssertCount = ++assertCount;
68 
69     gpushareddataWriteFinish(g_pGpu);
70 }
71 
72 /*
73  * Helper function for NV_ASSERT_FAILED
74  */
75 void
76 nvAssertFailed(void)
77 {
78     _logAssertCount();
79     NV_JOURNAL_ASSERT_FAILURE(NV_RM_ASSERT_UNKNOWN_LINE_NUM, portUtilGetReturnAddress());
80 }
81 
82 void
83 nvAssertOkFailed(NvU32 status)
84 {
85     _logAssertCount();
86     NV_JOURNAL_ASSERT_FAILURE_STATUS(NV_RM_ASSERT_UNKNOWN_LINE_NUM, portUtilGetReturnAddress(), status);
87 }
88 #endif
89 
90 #else //defined(GSP_PLUGIN_BUILD) || (defined(NVRM) && NVCPU_IS_RISCV64)
91 
92 #if NV_ASSERT_FAILED_USES_STRINGS
93 #define NV_ASSERT_FAILED_PRINTF_FMT                  "%s @ %s:%d\n"
94 #define NV_ASSERT_FAILED_PRINTF_PARAM                pszExpr, trimFN(pszFileName), lineNum
95 #else
96 #define NV_ASSERT_FAILED_PRINTF_FMT                  "0x%016llx\n"
97 #define NV_ASSERT_FAILED_PRINTF_PARAM                ip
98 #endif
99 
100 #define NV_ASSERT_PRINTF(level, fmt, ...)            NV_PRINTF_STRING          \
101     (NV_PRINTF_MODULE, level, NV_PRINTF_ADD_PREFIX(fmt), ##__VA_ARGS__)
102 
103 #define PATH_SEP   '/'
104 
105 /*
106  * Trim path from source filename.
107  */
108 #if NV_ASSERT_FAILED_USES_STRINGS
109 static const char *trimFN(const char *pszFileName)
110 {
111     NvLength i;
112 
113     for (i = 0; pszFileName[i] != 0; i++)
114         ;
115 
116     for (; i > 0; i--)
117     {
118         if (pszFileName[i] == PATH_SEP)
119             return &pszFileName[i + 1];
120     }
121 
122     return pszFileName;
123 }
124 #endif
125 
126 /*
127  * Helper function for NV_ASSERT_FAILED
128  */
129 void
130 nvAssertFailed
131 (
132     NV_ASSERT_FAILED_FUNC_TYPE
133 )
134 {
135     NvU64 ip = portUtilGetReturnAddress();
136     PORT_UNREFERENCED_VARIABLE(ip);
137 
138     NV_ASSERT_PRINTF(LEVEL_ERROR, "Assertion failed: " NV_ASSERT_FAILED_PRINTF_FMT,
139         NV_ASSERT_FAILED_PRINTF_PARAM);
140     NV_ASSERT_LOG(LEVEL_ERROR, "Assertion failed @ 0x%016x", ip);
141     NV_JOURNAL_ASSERT_FAILURE(lineNum, ip);
142 }
143 
144 /*
145  * Helper functions for NV_ASSERT_OK_FAILED
146  */
147 void
148 nvAssertOkFailed
149 (
150     NvU32       status
151     NV_ASSERT_FAILED_FUNC_COMMA_TYPE
152 )
153 {
154     NvU64 ip = portUtilGetReturnAddress();
155     PORT_UNREFERENCED_VARIABLE(ip);
156 
157     NV_ASSERT_PRINTF(LEVEL_ERROR,
158         "Assertion failed: %s (0x%08X) returned from " NV_ASSERT_FAILED_PRINTF_FMT,
159         nvAssertStatusToString(status), status, NV_ASSERT_FAILED_PRINTF_PARAM);
160     NV_ASSERT_LOG(LEVEL_ERROR, "Assertion failed: 0x%08X returned from 0x%016llx",
161         status, ip);
162     NV_JOURNAL_ASSERT_FAILURE_STATUS(lineNum, ip, status);
163 }
164 
165 /*
166  * Helper function for NV_CHECK_FAILED
167  */
168 void
169 nvCheckFailed
170 (
171     NvU32       level
172     NV_ASSERT_FAILED_FUNC_COMMA_TYPE
173 )
174 {
175     NvU64 ip = portUtilGetReturnAddress();
176     PORT_UNREFERENCED_VARIABLE(ip);
177 
178     NV_ASSERT_PRINTF(level, "Check failed: " NV_ASSERT_FAILED_PRINTF_FMT,
179         NV_ASSERT_FAILED_PRINTF_PARAM);
180     NV_ASSERT_LOG(level, "Check failed @ 0x%016llx", ip);
181 }
182 
183 /*
184  * Helper function for NV_CHECK_OK_FAILED
185  */
186 void
187 nvCheckOkFailed
188 (
189     NvU32       level,
190     NvU32       status
191     NV_ASSERT_FAILED_FUNC_COMMA_TYPE
192 )
193 {
194     NvU64 ip = portUtilGetReturnAddress();
195     PORT_UNREFERENCED_VARIABLE(ip);
196 
197     NV_ASSERT_PRINTF(level,
198         "Check failed: %s (0x%08X) returned from " NV_ASSERT_FAILED_PRINTF_FMT,
199         nvAssertStatusToString(status), status, NV_ASSERT_FAILED_PRINTF_PARAM);
200     NV_ASSERT_LOG(level, "Check failed: 0x%08X returned from 0x%016llx", status, ip);
201 }
202 
203 /*
204  * Helper function for NV_ASSERT_FAILED
205  */
206 void
207 nvAssertFailedNoLog
208 (
209     NV_ASSERT_FAILED_FUNC_TYPE
210 )
211 {
212     NvU64 ip = portUtilGetReturnAddress();
213     PORT_UNREFERENCED_VARIABLE(ip);
214 
215     NV_ASSERT_PRINTF(LEVEL_ERROR, "Assertion failed: " NV_ASSERT_FAILED_PRINTF_FMT,
216         NV_ASSERT_FAILED_PRINTF_PARAM);
217     NV_JOURNAL_ASSERT_FAILURE(lineNum, ip);
218 }
219 
220 /*
221  * Helper function for NV_ASSERT_OK_FAILED
222  */
223 void
224 nvAssertOkFailedNoLog
225 (
226     NvU32       status
227     NV_ASSERT_FAILED_FUNC_COMMA_TYPE
228 )
229 {
230     NvU64 ip = portUtilGetReturnAddress();
231     PORT_UNREFERENCED_VARIABLE(ip);
232 
233     NV_ASSERT_PRINTF(LEVEL_ERROR,
234         "Assertion failed: %s (0x%08X) returned from " NV_ASSERT_FAILED_PRINTF_FMT,
235         nvAssertStatusToString(status), status, NV_ASSERT_FAILED_PRINTF_PARAM);
236     NV_JOURNAL_ASSERT_FAILURE_STATUS(lineNum, ip, status);
237 }
238 
239 /*
240  * Helper function for NV_CHECK_FAILED
241  */
242 void
243 nvCheckFailedNoLog
244 (
245     NvU32       level
246     NV_ASSERT_FAILED_FUNC_COMMA_TYPE
247 )
248 {
249     NvU64 ip = portUtilGetReturnAddress();
250     PORT_UNREFERENCED_VARIABLE(ip);
251 
252     NV_ASSERT_PRINTF(level, "Check failed: " NV_ASSERT_FAILED_PRINTF_FMT,
253         NV_ASSERT_FAILED_PRINTF_PARAM);
254 }
255 
256 /*
257  * Helper function for NV_CHECK_OK_FAILED
258  */
259 void
260 nvCheckOkFailedNoLog
261 (
262     NvU32       level,
263     NvU32       status
264     NV_ASSERT_FAILED_FUNC_COMMA_TYPE
265 )
266 {
267     NvU64 ip = portUtilGetReturnAddress();
268     PORT_UNREFERENCED_VARIABLE(ip);
269 
270     NV_ASSERT_PRINTF(level,
271         "Check failed: %s (0x%08X) returned from " NV_ASSERT_FAILED_PRINTF_FMT,
272         nvAssertStatusToString(status), status, NV_ASSERT_FAILED_PRINTF_PARAM);
273 }
274 
275 #endif // defined(GSP_PLUGIN_BUILD) || (defined(NVRM) && NVCPU_IS_RISCV64)
276 #endif // NV_PRINTF_ENABLED || NV_JOURNAL_ASSERT_ENABLE
277 
278 /*
279  * Temporarily duplicate the nvstatusToString code to nvAssertStatusToString.
280  *
281  * Ideally, nvassert.c and nvstatus.c should both be included in shared.nvmk.
282  * But nvstatus.c is already directly included in projects from multiple module
283  * branches that also include shared.nvmk.  It is going to take some serious
284  * cross-module magic to move it.
285  */
286 
287 #if !defined(NV_PRINTF_STRING_SECTION)
288 #if defined(NVRM) && NVCPU_IS_RISCV64
289 #define NV_PRINTF_STRING_SECTION         __attribute__ ((section (".logging")))
290 #else // defined(NVRM) && NVCPU_IS_RISCV64
291 #define NV_PRINTF_STRING_SECTION
292 #endif // defined(NVRM) && NVCPU_IS_RISCV64
293 #endif // !defined(NV_PRINTF_STRING_SECTION)
294 
295 #undef NV_STATUS_CODE
296 #undef SDK_NVSTATUSCODES_H
297 #define NV_STATUS_CODE( name, code, string ) static NV_PRINTF_STRING_SECTION   \
298     const char rm_pvt_##name##_str[] = string " [" #name "]";
299 #include "nvstatuscodes.h"
300 
301 #undef NV_STATUS_CODE
302 #undef SDK_NVSTATUSCODES_H
303 #define NV_STATUS_CODE( name, code, string ) { name, rm_pvt_##name##_str },
304 static struct NvStatusCodeString
305 {
306     NV_STATUS   statusCode;
307     const char *statusString;
308 } g_StatusCodeList[] = {
309    #include "nvstatuscodes.h"
310    { 0xffffffff, "Unknown error code!" } // Some compilers don't like the trailing ','
311 };
312 #undef NV_STATUS_CODE
313 
314 /*!
315  * @brief Given an NV_STATUS code, returns the corresponding status string.
316  *
317  * @param[in]   nvStatusIn                  NV_STATUS code for which the string is required
318  *
319  * @returns     Corresponding status string from the nvstatuscodes.h
320  *
321  * TODO: Bug 200025711: convert this to an array-indexed lookup, instead of a linear search
322  *
323 */
324 const char *nvAssertStatusToString(NV_STATUS nvStatusIn)
325 {
326     static NV_PRINTF_STRING_SECTION const char rm_pvt_UNKNOWN_str[] = "Unknown error code!";
327     NvU32 i;
328     NvU32 n = ((NvU32)(sizeof(g_StatusCodeList))/(NvU32)(sizeof(g_StatusCodeList[0])));
329     for (i = 0; i < n; i++)
330     {
331         if (g_StatusCodeList[i].statusCode == nvStatusIn)
332         {
333             return g_StatusCodeList[i].statusString;
334         }
335     }
336 
337     return rm_pvt_UNKNOWN_str;
338 }
339 
340 #if defined(NV_ASSERT_FAILED_BACKTRACE)
341 MAKE_MAP(AssertedIPMap, NvU8);
342 
343 static struct
344 {
345     AssertedIPMap map;
346     NvU32 mode;
347     PORT_MUTEX *mtx;
348     NvBool init;
349     OS_THREAD_HANDLE tid;
350 } osAssertInternal;
351 
352 void nvAssertInit(void)
353 {
354     if (osAssertInternal.init)
355         return;
356 
357     osAssertInternal.mode = NV_REG_STR_RM_PRINT_ASSERT_BACKTRACE_UNIQUE;
358 
359     // Map is not thread-safe and osAssertFailed can be called concurrently.
360     osReadRegistryDword(NULL, NV_REG_STR_RM_PRINT_ASSERT_BACKTRACE, &osAssertInternal.mode);
361     if (osAssertInternal.mode == NV_REG_STR_RM_PRINT_ASSERT_BACKTRACE_UNIQUE)
362     {
363         osAssertInternal.mtx = portSyncMutexCreate(portMemAllocatorGetGlobalNonPaged());
364         if (!osAssertInternal.mtx)
365         {
366             osAssertInternal.mode = NV_REG_STR_RM_PRINT_ASSERT_BACKTRACE_DISABLE;
367         }
368         else
369         {
370             mapInit(&osAssertInternal.map, portMemAllocatorGetGlobalNonPaged());
371         }
372     }
373     osAssertInternal.init = NV_TRUE;
374 }
375 
376 static void nvAssertFailedBacktrace(NvU64 ip)
377 {
378     if (!osAssertInternal.init)
379         return;
380 
381     if (osAssertInternal.mode == NV_REG_STR_RM_PRINT_ASSERT_BACKTRACE_UNIQUE)
382     {
383         OS_THREAD_HANDLE tid;
384         if (osGetCurrentThread(&tid) != NV_OK)
385             return;
386 
387         // nvport mutex is not reentrant and will deadlock with nested locking.
388         // If the next condition holds, we're in a reentrant call.
389         if (tid == osAssertInternal.tid)
390             return;
391 
392         portSyncMutexAcquire(osAssertInternal.mtx);
393         osAssertInternal.tid = tid;
394 
395         if (!mapFind(&osAssertInternal.map, ip))
396         {
397             // If we're out of memory, do not dump anything to avoid spam
398             if (mapInsertNew(&osAssertInternal.map, ip))
399                 osAssertFailed();
400         }
401 
402         osAssertInternal.tid = 0;
403         portSyncMutexRelease(osAssertInternal.mtx);
404     }
405     else if (osAssertInternal.mode == NV_REG_STR_RM_PRINT_ASSERT_BACKTRACE_ENABLE)
406         osAssertFailed();
407 }
408 
409 void nvAssertDestroy(void)
410 {
411     if (!osAssertInternal.init)
412         return;
413 
414     if (osAssertInternal.mode == NV_REG_STR_RM_PRINT_ASSERT_BACKTRACE_UNIQUE && osAssertInternal.mtx)
415     {
416         portSyncMutexDestroy(osAssertInternal.mtx);
417         mapDestroy(&osAssertInternal.map);
418     }
419     osAssertInternal.init = 0;
420 }
421 
422 #elif defined(NVRM) && !defined(NVWATCH) // ignore in nvlog_decoder/nvwatch build
423 
424 // We do not expose NV_ASSERT_FAILED_BACKTRACE outside this file. The callers will use these stubs.
425 void nvAssertInit(void)
426 {
427 }
428 
429 void nvAssertDestroy(void)
430 {
431 }
432 #endif /* defined(NV_ASSERT_FAILED_BACKTRACE) */
433