1 /* 2 * SPDX-FileCopyrightText: Copyright (c) 2021-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 #include <core/locks.h> 25 #include <ctrl/ctrl0080/ctrl0080unix.h> 26 #include <gpu/device/device.h> 27 #include <gpu/gpu.h> 28 #include <nv-priv.h> 29 #include <nv.h> 30 #include <osapi.h> 31 #include <gpu/mem_mgr/mem_mgr.h> 32 #include <platform/sli/sli.h> 33 34 #include <vgpu/rpc.h> 35 #include "vgpu/vgpu_events.h" 36 37 static NV_STATUS 38 unixCallVideoBIOS 39 ( 40 OBJGPU *pGpu, 41 NvU32 *eax, 42 NvU32 *ebx 43 ) 44 { 45 NV_STATUS status = NV_ERR_NOT_SUPPORTED; 46 47 if (NVCPU_IS_X86_64) 48 { 49 NvU32 eax_in = *eax; 50 NvU32 ebx_in = *ebx; 51 52 if (pGpu->getProperty(pGpu, PDB_PROP_GPU_IS_UEFI)) 53 { 54 return NV_ERR_NOT_SUPPORTED; 55 } 56 57 NV_PRINTF(LEVEL_INFO, "unixCallVideoBIOS: 0x%x 0x%x, vga_satus = %d\n", *eax, *ebx, NV_PRIMARY_VGA(NV_GET_NV_STATE(pGpu))); 58 59 status = nv_vbios_call(pGpu, eax, ebx); 60 61 // this was originally changed for nt in changelist 644223 62 if (*eax != 0x4f) 63 { 64 NV_PRINTF(LEVEL_ERROR, 65 "int10h(%04x, %04x) vesa call failed! (%04x, %04x)\n", 66 eax_in, ebx_in, *eax, *ebx); 67 status = NV_ERR_GENERIC; 68 } 69 } 70 71 return status; 72 } 73 74 static void 75 RmSaveDisplayState 76 ( 77 OBJGPU *pGpu 78 ) 79 { 80 nv_state_t *nv = NV_GET_NV_STATE(pGpu); 81 nv_priv_t *nvp = NV_GET_NV_PRIV(nv); 82 RM_API *pRmApi = rmapiGetInterface(RMAPI_GPU_LOCK_INTERNAL); 83 KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu); 84 NvBool use_vbios = NV_PRIMARY_VGA(nv) && RmGpuHasIOSpaceEnabled(nv); 85 NvU32 eax, ebx; 86 NV_STATUS status; 87 NV2080_CTRL_CMD_INTERNAL_DISPLAY_PRE_UNIX_CONSOLE_PARAMS preUnixConsoleParams = {0}; 88 NV2080_CTRL_CMD_INTERNAL_DISPLAY_POST_UNIX_CONSOLE_PARAMS postUnixConsoleParams = {0}; 89 90 if (IS_VIRTUAL(pGpu) || pKernelDisplay == NULL) 91 { 92 return; 93 } 94 95 if (pGpu->getProperty(pGpu, PDB_PROP_GPU_IS_UEFI)) 96 { 97 NV_PRINTF(LEVEL_INFO, "RM fallback doesn't support saving of efifb console\n"); 98 return; 99 } 100 101 os_disable_console_access(); 102 103 preUnixConsoleParams.bSave = NV_TRUE; 104 preUnixConsoleParams.bUseVbios = use_vbios; 105 106 NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR,pRmApi->Control(pRmApi, nv->rmapi.hClient, nv->rmapi.hSubDevice, 107 NV2080_CTRL_CMD_INTERNAL_DISPLAY_PRE_UNIX_CONSOLE, 108 &preUnixConsoleParams, sizeof(preUnixConsoleParams)), done); 109 110 if (use_vbios) 111 { 112 // 113 // Attempt to identify the currently set VESA mode; assume 114 // vanilla VGA text if the VBIOS call fails. 115 // 116 eax = 0x4f03; 117 ebx = 0; 118 if (NV_OK == unixCallVideoBIOS(pGpu, &eax, &ebx)) 119 { 120 nvp->vga.vesaMode = (ebx & 0x3fff); 121 } 122 else 123 { 124 nvp->vga.vesaMode = 3; 125 } 126 } 127 128 postUnixConsoleParams.bSave = NV_TRUE; 129 postUnixConsoleParams.bUseVbios = use_vbios; 130 131 NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR, pRmApi->Control(pRmApi, nv->rmapi.hClient, 132 nv->rmapi.hSubDevice, 133 NV2080_CTRL_CMD_INTERNAL_DISPLAY_POST_UNIX_CONSOLE, 134 &postUnixConsoleParams, sizeof(postUnixConsoleParams)), done); 135 136 done: 137 os_enable_console_access(); 138 } 139 140 static void RmRestoreDisplayState 141 ( 142 OBJGPU *pGpu 143 ) 144 { 145 nv_state_t *nv = NV_GET_NV_STATE(pGpu); 146 nv_priv_t *nvp = NV_GET_NV_PRIV(nv); 147 RM_API *pRmApi = rmapiGetInterface(RMAPI_GPU_LOCK_INTERNAL); 148 NvBool use_vbios = NV_PRIMARY_VGA(nv) && RmGpuHasIOSpaceEnabled(nv);; 149 KernelDisplay *pKernelDisplay = GPU_GET_KERNEL_DISPLAY(pGpu); 150 NV_STATUS status; 151 NvU32 eax, ebx; 152 NV2080_CTRL_CMD_INTERNAL_DISPLAY_PRE_UNIX_CONSOLE_PARAMS preUnixConsoleParams = {0}; 153 NV2080_CTRL_CMD_INTERNAL_DISPLAY_POST_UNIX_CONSOLE_PARAMS postUnixConsoleParams = {0}; 154 155 NV_ASSERT_OR_RETURN_VOID(pKernelDisplay != NULL); 156 157 // 158 // vGPU: 159 // 160 // Since vGPU does all real hardware management in the 161 // host, there is nothing to do at this point in the 162 // guest OS (where IS_VIRTUAL(pGpu) is true). 163 // 164 if (IS_VIRTUAL(pGpu)) 165 { 166 // we don't have VGA state that's needing to be restored. 167 NV_PRINTF(LEVEL_INFO, "skipping RestoreDisplayState on VGPU (0x%x)\n", 168 pGpu->gpuId); 169 return; 170 } 171 172 // 173 // Fix up DCB index VBIOS scratch registers. 174 // The strategies employed are: 175 // 176 // SBIOS/VBIOS: 177 // Clear the DCB index, and set the previous DCB index to the original 178 // value. This allows the VBIOS (during the int10h mode-set) to 179 // determine which display to enable, and to set the head-enabled bit 180 // as needed (see bugs #264873 and #944398). 181 // 182 if (pGpu->getProperty(pGpu, PDB_PROP_GPU_IS_UEFI)) 183 { 184 NV_PRINTF(LEVEL_INFO, "RM fallback doesn't support efifb console restore\n"); 185 return; 186 } 187 188 os_disable_console_access(); 189 190 preUnixConsoleParams.bUseVbios = use_vbios; 191 preUnixConsoleParams.bSave = NV_FALSE; 192 193 NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR, pRmApi->Control(pRmApi, nv->rmapi.hClient, 194 nv->rmapi.hSubDevice, 195 NV2080_CTRL_CMD_INTERNAL_DISPLAY_PRE_UNIX_CONSOLE, 196 &preUnixConsoleParams, sizeof(preUnixConsoleParams)), done); 197 198 if (use_vbios) 199 { 200 eax = 0x4f02; 201 ebx = nvp->vga.vesaMode; 202 203 if (NV_OK == unixCallVideoBIOS(pGpu, &eax, &ebx)) 204 { 205 postUnixConsoleParams.bVbiosCallSuccessful = NV_TRUE; 206 } 207 } 208 209 postUnixConsoleParams.bSave = NV_FALSE; 210 postUnixConsoleParams.bUseVbios = use_vbios; 211 212 NV_CHECK_OK_OR_GOTO(status, LEVEL_ERROR, pRmApi->Control(pRmApi, nv->rmapi.hClient, 213 nv->rmapi.hSubDevice, 214 NV2080_CTRL_CMD_INTERNAL_DISPLAY_POST_UNIX_CONSOLE, 215 &postUnixConsoleParams, sizeof(postUnixConsoleParams)), done); 216 217 done: 218 os_enable_console_access(); 219 } 220 221 static void 222 RmChangeResMode 223 ( 224 OBJGPU *pGpu, 225 NvBool hires 226 ) 227 { 228 if (hires) 229 { 230 SLI_LOOP_START(SLI_LOOP_FLAGS_NONE) 231 232 RmSaveDisplayState(pGpu); 233 234 SLI_LOOP_END 235 } 236 else 237 { 238 SLI_LOOP_START(SLI_LOOP_FLAGS_NONE) 239 240 RmRestoreDisplayState(pGpu); 241 // 242 // vGPU: 243 // 244 // Since vGPU does all real hardware management in the host, if we 245 // are in guest OS (where IS_VIRTUAL(pGpu) is true), do an RPC to 246 // the host to trigger switch from HIRES to (LORES)VGA. 247 // 248 if (IS_VIRTUAL(pGpu)) 249 { 250 NV_STATUS status = NV_OK; 251 NV_RM_RPC_SWITCH_TO_VGA(pGpu, status); 252 } 253 254 SLI_LOOP_END 255 } 256 } 257 258 NV_STATUS NV_API_CALL 259 rm_save_low_res_mode 260 ( 261 nvidia_stack_t *sp, 262 nv_state_t *pNv 263 ) 264 { 265 THREAD_STATE_NODE threadState; 266 OBJGPU *pGpu = NV_GET_NV_PRIV_PGPU(pNv); 267 void *fp; 268 269 NV_ENTER_RM_RUNTIME(sp,fp); 270 threadStateInit(&threadState, THREAD_STATE_FLAGS_NONE); 271 272 RmSaveDisplayState(pGpu); 273 274 threadStateFree(&threadState, THREAD_STATE_FLAGS_NONE); 275 NV_EXIT_RM_RUNTIME(sp,fp); 276 277 return NV_OK; 278 } 279 280 NV_STATUS 281 deviceCtrlCmdOsUnixVTSwitch_IMPL 282 ( 283 Device *pDevice, 284 NV0080_CTRL_OS_UNIX_VT_SWITCH_PARAMS *pParams 285 ) 286 { 287 OBJGPU *pGpu = GPU_RES_GET_GPU(pDevice); 288 nv_state_t *nv = NV_GET_NV_STATE(pGpu); 289 NvBool hires; 290 NvBool bChangeResMode = NV_TRUE; 291 292 switch (pParams->cmd) 293 { 294 case NV0080_CTRL_OS_UNIX_VT_SWITCH_CMD_SAVE_VT_STATE: 295 hires = NV_TRUE; 296 break; 297 298 case NV0080_CTRL_OS_UNIX_VT_SWITCH_CMD_RESTORE_VT_STATE: 299 hires = NV_FALSE; 300 break; 301 302 case NV0080_CTRL_OS_UNIX_VT_SWITCH_CMD_CONSOLE_RESTORED: 303 bChangeResMode = NV_FALSE; 304 break; 305 306 default: 307 return NV_ERR_INVALID_ARGUMENT; 308 } 309 310 RmUpdateGc6ConsoleRefCount(nv, 311 pParams->cmd != NV0080_CTRL_OS_UNIX_VT_SWITCH_CMD_SAVE_VT_STATE); 312 313 if (!bChangeResMode) 314 { 315 return NV_OK; 316 } 317 318 if (rmGpuLocksAcquire(GPUS_LOCK_FLAGS_NONE, RM_LOCK_MODULES_FB) == NV_OK) 319 { 320 RmChangeResMode(pGpu, hires); 321 rmGpuLocksRelease(GPUS_LOCK_FLAGS_NONE, NULL); 322 } 323 else 324 { 325 NV_PRINTF(LEVEL_INFO,"%s: Failed to acquire GPU lock", __FUNCTION__); 326 } 327 return NV_OK; 328 } 329 330 NV_STATUS deviceCtrlCmdOsUnixVTGetFBInfo_IMPL 331 ( 332 Device *pDevice, 333 NV0080_CTRL_OS_UNIX_VT_GET_FB_INFO_PARAMS *pParams 334 ) 335 { 336 OBJGPU *pGpu = GPU_RES_GET_GPU(pDevice); 337 NvBool bContinue = NV_TRUE; 338 339 if (rmGpuLocksAcquire(GPUS_LOCK_FLAGS_NONE, RM_LOCK_MODULES_FB) == NV_OK) 340 { 341 // See if the console is on one of the subdevices of this device. 342 portMemSet(pParams, 0, sizeof(*pParams)); 343 344 SLI_LOOP_START(SLI_LOOP_FLAGS_NONE) 345 346 MemoryManager *pMemoryManager = GPU_GET_MEMORY_MANAGER(pGpu); 347 nv_state_t *nv = NV_GET_NV_STATE(pGpu); 348 349 if ((memmgrGetReservedConsoleMemDesc(pGpu, pMemoryManager) != NULL) && bContinue) 350 { 351 NvU64 baseAddr; 352 NvU32 width, height, depth, pitch; 353 354 // There should only be one. 355 NV_ASSERT(pParams->width == 0); 356 357 pParams->subDeviceInstance = gpumgrGetSubDeviceInstanceFromGpu(pGpu); 358 359 // Console is either mapped to BAR1 or BAR2 + 16 MB 360 os_get_screen_info(&baseAddr, &width, &height, &depth, &pitch, 361 nv->bars[NV_GPU_BAR_INDEX_FB].cpu_address, 362 nv->bars[NV_GPU_BAR_INDEX_IMEM].cpu_address + 0x1000000); 363 364 pParams->width = (NvU16)width; 365 pParams->height = (NvU16)height; 366 pParams->depth = (NvU16)depth; 367 pParams->pitch = (NvU16)pitch; 368 369 if (baseAddr != 0) 370 { 371 bContinue = NV_FALSE; 372 } 373 } 374 375 SLI_LOOP_END 376 377 rmGpuLocksRelease(GPUS_LOCK_FLAGS_NONE, NULL); 378 } 379 else 380 { 381 NV_PRINTF(LEVEL_INFO,"%s: Failed to acquire GPU lock", __FUNCTION__); 382 } 383 384 return NV_OK; 385 } 386 387 388 void 389 dispdeviceFillVgaSavedDisplayState 390 ( 391 OBJGPU *pGpu, 392 NvU64 vgaAddr, 393 NvU8 vgaMemType, 394 NvBool vgaValid, 395 NvU64 workspaceAddr, 396 NvU8 workspaceMemType, 397 NvBool workspaceValid, 398 NvBool baseValid, 399 NvBool workspaceBaseValid 400 ) 401 { 402 nv_state_t *nv = NV_GET_NV_STATE(pGpu); 403 nv_priv_t *nvp = NV_GET_NV_PRIV(nv); 404 NvBool use_vbios = NV_PRIMARY_VGA(nv) && RmGpuHasIOSpaceEnabled(nv); 405 406 if (use_vbios) 407 { 408 nvp->vga.base.addr = vgaAddr; 409 nvp->vga.base.memTarget = vgaMemType; 410 nvp->vga.base.valid = vgaValid; 411 nvp->vga.baseValid = baseValid; 412 413 nvp->vga.workspaceBase.addr = workspaceAddr; 414 nvp->vga.workspaceBase.memTarget = workspaceMemType; 415 nvp->vga.workspaceBase.valid = workspaceValid; 416 nvp->vga.workspaceBaseValid = workspaceBaseValid; 417 } 418 } 419 420