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
unixCallVideoBIOS(OBJGPU * pGpu,NvU32 * eax,NvU32 * ebx)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
RmSaveDisplayState(OBJGPU * pGpu)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
RmRestoreDisplayState(OBJGPU * pGpu)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
RmChangeResMode(OBJGPU * pGpu,NvBool hires)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
rm_save_low_res_mode(nvidia_stack_t * sp,nv_state_t * pNv)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
deviceCtrlCmdOsUnixVTSwitch_IMPL(Device * pDevice,NV0080_CTRL_OS_UNIX_VT_SWITCH_PARAMS * pParams)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
deviceCtrlCmdOsUnixVTGetFBInfo_IMPL(Device * pDevice,NV0080_CTRL_OS_UNIX_VT_GET_FB_INFO_PARAMS * pParams)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, size;
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 nv_get_screen_info(nv, &baseAddr, &width, &height, &depth,
361 &pitch, &size);
362
363 pParams->width = (NvU16)width;
364 pParams->height = (NvU16)height;
365 pParams->depth = (NvU16)depth;
366 pParams->pitch = (NvU16)pitch;
367
368 if (baseAddr != 0)
369 {
370 bContinue = NV_FALSE;
371 }
372 }
373
374 SLI_LOOP_END
375
376 rmGpuLocksRelease(GPUS_LOCK_FLAGS_NONE, NULL);
377 }
378 else
379 {
380 NV_PRINTF(LEVEL_INFO,"%s: Failed to acquire GPU lock", __FUNCTION__);
381 }
382
383 return NV_OK;
384 }
385
386
387 void
dispdeviceFillVgaSavedDisplayState(OBJGPU * pGpu,NvU64 vgaAddr,NvU8 vgaMemType,NvBool vgaValid,NvU64 workspaceAddr,NvU8 workspaceMemType,NvBool workspaceValid,NvBool baseValid,NvBool workspaceBaseValid)388 dispdeviceFillVgaSavedDisplayState
389 (
390 OBJGPU *pGpu,
391 NvU64 vgaAddr,
392 NvU8 vgaMemType,
393 NvBool vgaValid,
394 NvU64 workspaceAddr,
395 NvU8 workspaceMemType,
396 NvBool workspaceValid,
397 NvBool baseValid,
398 NvBool workspaceBaseValid
399 )
400 {
401 nv_state_t *nv = NV_GET_NV_STATE(pGpu);
402 nv_priv_t *nvp = NV_GET_NV_PRIV(nv);
403 NvBool use_vbios = NV_PRIMARY_VGA(nv) && RmGpuHasIOSpaceEnabled(nv);
404
405 if (use_vbios)
406 {
407 nvp->vga.base.addr = vgaAddr;
408 nvp->vga.base.memTarget = vgaMemType;
409 nvp->vga.base.valid = vgaValid;
410 nvp->vga.baseValid = baseValid;
411
412 nvp->vga.workspaceBase.addr = workspaceAddr;
413 nvp->vga.workspaceBase.memTarget = workspaceMemType;
414 nvp->vga.workspaceBase.valid = workspaceValid;
415 nvp->vga.workspaceBaseValid = workspaceBaseValid;
416 }
417 }
418
419