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