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