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