VirtualBox

Changeset 92399 in vbox


Ignore:
Timestamp:
Nov 12, 2021 2:28:15 PM (3 years ago)
Author:
vboxsync
Message:

Additions: Linux: VBoxDRMClient: cache and validate incomming screen layout data, bugref:10134.

This commit is mostly about refactoring. It additionally introduces caching of
incoming (from host) screen layout data and validates it before feeding DRM stack.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/x11/VBoxClient/display-drm.cpp

    r92370 r92399  
    117117unsigned g_cVerbosity = 0;
    118118
     119/** Path to the PID file. */
     120static const char g_szPidFile[RTPATH_MAX] = "/var/run/VBoxDRMClient";
     121
    119122/**
    120123 * Attempts to open DRM device by given path and check if it is
     
    210213}
    211214
     215/**
     216 * This function converts input monitors layout array passed from DevVMM
     217 * into monitors layout array to be passed to DRM stack.
     218 *
     219 * @return  VINF_SUCCESS on success, IPRT error code otherwise.
     220 * @param   aDisplaysIn         Input displays array.
     221 * @param   cDisplaysIn         Number of elements in input displays array.
     222 * @param   aDisplaysOut        Output displays array.
     223 * @param   cDisplaysOutMax     Number of elements in output displays array.
     224 * @param   pcActualDisplays    Number of displays to report to DRM stack (number of enabled displays).
     225 */
     226static int drmValidateLayout(VMMDevDisplayDef *aDisplaysIn, uint32_t cDisplaysIn,
     227                             struct DRMVMWRECT *aDisplaysOut, uint32_t cDisplaysOutMax, uint32_t *pcActualDisplays)
     228{
     229    /* This array is a cache of what was received from DevVMM so far.
     230     * DevVMM may send to us partial information bout scree layout. This
     231     * cache remembers entire picture. */
     232    static struct VMMDevDisplayDef aVmMonitorsCache[VMW_MAX_HEADS];
     233    /* Number of valid (enabled) displays in output array. */
     234    uint32_t cDisplaysOut = 0;
     235    /* Flag indicates that current layout cache is consistent and can be passed to DRM stack. */
     236    bool fValid = true;
     237
     238    /* Make sure input array fits cache size. */
     239    if (cDisplaysIn > VMW_MAX_HEADS)
     240    {
     241        VBClLogError("VBoxDRMClient: unable to validate screen layout: input (%u) array does not fit to cache size (%u)\n",
     242                     cDisplaysIn, VMW_MAX_HEADS);
     243        return VERR_INVALID_PARAMETER;
     244    }
     245
     246    /* Make sure there is enough space in output array. */
     247    if (cDisplaysIn > cDisplaysOutMax)
     248    {
     249        VBClLogError("VBoxDRMClient: unable to validate screen layout: input array (%u) is bigger than output one (%u)\n",
     250                     cDisplaysIn, cDisplaysOut);
     251        return VERR_INVALID_PARAMETER;
     252    }
     253
     254    /* Make sure input and output arrays are of non-zero size. */
     255    if (!(cDisplaysIn > 0 && cDisplaysOutMax > 0))
     256    {
     257        VBClLogError("VBoxDRMClient: unable to validate screen layout: invalid size of either input (%u) or output display array\n",
     258                     cDisplaysIn, cDisplaysOutMax);
     259        return VERR_INVALID_PARAMETER;
     260    }
     261
     262    /* Update cache. */
     263    for (uint32_t i = 0; i < cDisplaysIn; i++)
     264    {
     265        uint32_t idDisplay = aDisplaysIn[i].idDisplay;
     266        if (idDisplay < VMW_MAX_HEADS)
     267        {
     268            aVmMonitorsCache[idDisplay].idDisplay = idDisplay;
     269            aVmMonitorsCache[idDisplay].fDisplayFlags = aDisplaysIn[i].fDisplayFlags;
     270            aVmMonitorsCache[idDisplay].cBitsPerPixel = aDisplaysIn[i].cBitsPerPixel;
     271            aVmMonitorsCache[idDisplay].cx = aDisplaysIn[i].cx;
     272            aVmMonitorsCache[idDisplay].cy = aDisplaysIn[i].cy;
     273            aVmMonitorsCache[idDisplay].xOrigin = aDisplaysIn[i].xOrigin;
     274            aVmMonitorsCache[idDisplay].yOrigin = aDisplaysIn[i].yOrigin;
     275        }
     276        else
     277        {
     278            VBClLogError("VBoxDRMClient: received display ID (0x%x, position %u) is invalid\n", idDisplay, i);
     279            /* If monitor configuration cannot be placed into cache, consider entire cache is invalid. */
     280            fValid = false;
     281        }
     282    }
     283
     284    /* Now, go though complete cache and check if it is valid. */
     285    for (uint32_t i = 0; i < VMW_MAX_HEADS; i++)
     286    {
     287        if (i == 0)
     288        {
     289            if (aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
     290            {
     291                VBClLogError("VBoxDRMClient: unable to validate screen layout: first monitor is not allowed to be disabled");
     292                fValid = false;
     293            }
     294            else
     295                cDisplaysOut++;
     296        }
     297        else
     298        {
     299            /* Check if there is no hole in between monitors (i.e., if current monitor is enabled, but privious one does not). */
     300            if (   !(aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
     301                && aVmMonitorsCache[i - 1].fDisplayFlags & VMMDEV_DISPLAY_DISABLED)
     302            {
     303                VBClLogError("VBoxDRMClient: unable to validate screen layout: there is a hole in displays layout config, "
     304                             "monitor (%u) is ENABLED while (%u) does not\n", i, i - 1);
     305                fValid = false;
     306            }
     307            else
     308            {
     309                /* Align displays next to each other (if needed) and check if there is no holes in between monitors. */
     310                if (!(aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
     311                {
     312                    aVmMonitorsCache[i].xOrigin = aVmMonitorsCache[i - 1].xOrigin + aVmMonitorsCache[i - 1].cx;
     313                    aVmMonitorsCache[i].yOrigin = aVmMonitorsCache[i - 1].yOrigin;
     314                }
     315
     316                /* Only count enabled monitors. */
     317                if (!(aVmMonitorsCache[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
     318                    cDisplaysOut++;
     319            }
     320        }
     321    }
     322
     323    /* Copy out layout data. */
     324    if (fValid)
     325    {
     326        for (uint32_t i = 0; i < cDisplaysOut; i++)
     327        {
     328            aDisplaysOut[i].x = aVmMonitorsCache[i].xOrigin;
     329            aDisplaysOut[i].y = aVmMonitorsCache[i].yOrigin;
     330            aDisplaysOut[i].w = aVmMonitorsCache[i].cx;
     331            aDisplaysOut[i].h = aVmMonitorsCache[i].cy;
     332
     333            VBClLogInfo("VBoxDRMClient: update monitor %u parameters: %dx%d, (%d, %d)\n",
     334                        i,
     335                        aDisplaysOut[i].w, aDisplaysOut[i].h,
     336                        aDisplaysOut[i].x, aDisplaysOut[i].y);
     337
     338        }
     339
     340        *pcActualDisplays = cDisplaysOut;
     341    }
     342
     343    return (fValid && cDisplaysOut > 0) ? VINF_SUCCESS : VERR_INVALID_PARAMETER;
     344}
     345
    212346static void drmSendHints(RTFILE hDevice, struct DRMVMWRECT *paRects, unsigned cHeads)
    213347{
     348    int rc;
     349    struct DRMVMWUPDATELAYOUT ioctlLayout;
    214350    uid_t curuid = getuid();
    215351    if (setreuid(0, 0) != 0)
    216         perror("setreuid failed during drm ioctl.");
    217     int rc;
    218     struct DRMVMWUPDATELAYOUT ioctlLayout;
     352    {
     353        VBClLogError("VBoxDRMClient: setreuid failed during drm ioctl\n");
     354        return;
     355    }
    219356
    220357    ioctlLayout.cOutputs = cHeads;
    221358    ioctlLayout.ptrRects = (uint64_t)paRects;
     359
    222360    rc = RTFileIoCtl(hDevice, DRM_IOCTL_VMW_UPDATE_LAYOUT,
    223361                     &ioctlLayout, sizeof(ioctlLayout), NULL);
    224     if (RT_FAILURE(rc) && rc != VERR_INVALID_PARAMETER)
    225         VBClLogFatalError("Failure updating layout, rc=%Rrc\n", rc);
     362
     363    VBClLogInfo("VBoxDRMClient: push layout data to DRM stack %s, %Rrc\n",
     364                RT_SUCCESS(rc) ? "successful" : "failed", rc);
     365
    226366    if (setreuid(curuid, 0) != 0)
    227         perror("reset of setreuid failed after drm ioctl.");
     367    {
     368        VBClLogError("VBoxDRMClient: reset of setreuid failed after drm ioctl");
     369    }
    228370}
    229371
    230 int main(int argc, char *argv[])
    231 {
    232     RTFILE hDevice = NIL_RTFILE;
    233     static struct VMMDevDisplayDef aMonitors[VMW_MAX_HEADS];
    234     unsigned cEnabledMonitors;
    235     /* Do not acknowledge the first event we query for to pick up old events,
    236      * e.g. from before a guest reboot. */
    237     bool fAck = false;
    238 
    239     /** The name and handle of the PID file. */
    240     static const char szPidFile[RTPATH_MAX] = "/var/run/VBoxDRMClient";
    241     RTFILE hPidFile;
    242 
    243     int rc = RTR3InitExe(argc, &argv, 0);
    244     if (RT_FAILURE(rc))
    245         return RTMsgInitFailure(rc);
    246 
    247     rc = VbglR3InitUser();
    248     if (RT_FAILURE(rc))
    249         VBClLogFatalError("VbglR3InitUser failed: %Rrc", rc);
    250 
    251     /* Check PID file before attempting to initialize anything. */
    252     rc = VbglR3PidFile(szPidFile, &hPidFile);
    253     if (rc == VERR_FILE_LOCK_VIOLATION)
    254     {
    255         VBClLogInfo("VBoxDRMClient: already running, exiting\n");
    256         return RTEXITCODE_SUCCESS;
    257     }
    258     else if (RT_FAILURE(rc))
    259     {
    260         VBClLogError("VBoxDRMClient: unable to lock PID file (%Rrc), exiting\n", rc);
    261         return RTEXITCODE_FAILURE;
    262     }
    263 
    264     hDevice = drmOpenVmwgfx();
    265     if (hDevice == NIL_RTFILE)
    266         return VERR_OPEN_FAILED;
    267 
    268     rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
    269     if (RT_FAILURE(rc))
    270     {
    271         VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
    272         return VERR_INVALID_HANDLE;
    273     }
    274     rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
    275     if (rc == VERR_RESOURCE_BUSY)  /* Someone else has already acquired it. */
    276     {
    277         return VERR_RESOURCE_BUSY;
    278     }
    279     if (RT_FAILURE(rc))
    280     {
    281         VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
    282         return VERR_INVALID_HANDLE;
    283     }
     372static void drmMainLoop(RTFILE hDevice)
     373{
     374    int rc;
    284375
    285376    for (;;)
    286377    {
     378        /* Do not acknowledge the first event we query for to pick up old events,
     379         * e.g. from before a guest reboot. */
     380        bool fAck = false;
     381
    287382        uint32_t events;
    288         struct VMMDevDisplayDef aDisplays[VMW_MAX_HEADS];
    289         uint32_t cDisplaysOut;
     383
     384        VMMDevDisplayDef aDisplaysIn[VMW_MAX_HEADS];
     385        uint32_t cDisplaysIn = 0;
     386
     387        struct DRMVMWRECT aDisplaysOut[VMW_MAX_HEADS];
     388        uint32_t cDisplaysOut = 0;
     389
     390        RT_ZERO(aDisplaysIn);
     391        RT_ZERO(aDisplaysOut);
     392
    290393        /* Query the first size without waiting.  This lets us e.g. pick up
    291394         * the last event before a guest reboot when we start again after. */
    292         rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysOut, aDisplays, fAck);
     395        rc = VbglR3GetDisplayChangeRequestMulti(VMW_MAX_HEADS, &cDisplaysIn, aDisplaysIn, fAck);
    293396        fAck = true;
    294397        if (RT_FAILURE(rc))
     398        {
    295399            VBClLogError("Failed to get display change request, rc=%Rrc\n", rc);
    296         if (cDisplaysOut > VMW_MAX_HEADS)
    297             VBClLogError("Display change request contained, rc=%Rrc\n", rc);
    298         if (cDisplaysOut > 0)
    299         {
    300             for (unsigned i = 0; i < cDisplaysOut && i < VMW_MAX_HEADS; ++i)
    301             {
    302                 uint32_t idDisplay = aDisplays[i].idDisplay;
    303                 if (idDisplay >= VMW_MAX_HEADS)
    304                     continue;
    305                 aMonitors[idDisplay].fDisplayFlags = aDisplays[i].fDisplayFlags;
    306                 if (!(aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
    307                 {
    308                     if ((idDisplay == 0) || (aDisplays[i].fDisplayFlags & VMMDEV_DISPLAY_ORIGIN))
    309                     {
    310                         aMonitors[idDisplay].xOrigin = aDisplays[i].xOrigin;
    311                         aMonitors[idDisplay].yOrigin = aDisplays[i].yOrigin;
    312                     } else {
    313                         aMonitors[idDisplay].xOrigin = aMonitors[idDisplay - 1].xOrigin + aMonitors[idDisplay - 1].cx;
    314                         aMonitors[idDisplay].yOrigin = aMonitors[idDisplay - 1].yOrigin;
    315                     }
    316                     aMonitors[idDisplay].cx = aDisplays[i].cx;
    317                     aMonitors[idDisplay].cy = aDisplays[i].cy;
    318                 }
    319             }
    320             /* Create an dense (consisting of enabled monitors only) array to pass to DRM. */
    321             cEnabledMonitors = 0;
    322             struct DRMVMWRECT aEnabledMonitors[VMW_MAX_HEADS];
    323             for (int j = 0; j < VMW_MAX_HEADS; ++j)
    324             {
    325                 if (!(aMonitors[j].fDisplayFlags & VMMDEV_DISPLAY_DISABLED))
    326                 {
    327                     aEnabledMonitors[cEnabledMonitors].x = aMonitors[j].xOrigin;
    328                     aEnabledMonitors[cEnabledMonitors].y = aMonitors[j].yOrigin;
    329                     aEnabledMonitors[cEnabledMonitors].w = aMonitors[j].cx;
    330                     aEnabledMonitors[cEnabledMonitors].h = aMonitors[j].cy;
    331                     if (cEnabledMonitors > 0)
    332                         aEnabledMonitors[cEnabledMonitors].x = aEnabledMonitors[cEnabledMonitors - 1].x + aEnabledMonitors[cEnabledMonitors - 1].w;
    333                     ++cEnabledMonitors;
    334                 }
    335             }
    336             for (unsigned i = 0; i < cEnabledMonitors; ++i)
    337                 printf("Monitor %u: %dx%d, (%d, %d)\n", i, (int)aEnabledMonitors[i].w, (int)aEnabledMonitors[i].h,
    338                        (int)aEnabledMonitors[i].x, (int)aEnabledMonitors[i].y);
    339             drmSendHints(hDevice, aEnabledMonitors, cEnabledMonitors);
    340         }
     400        }
     401        else
     402        {
     403            /* Validate displays layout and push it to DRM stack if valid. */
     404            rc = drmValidateLayout(aDisplaysIn, cDisplaysIn, aDisplaysOut, sizeof(aDisplaysOut), &cDisplaysOut);
     405            if (RT_SUCCESS(rc))
     406            {
     407                VBClLogInfo("VBoxDRMClient: push to DRM stack info about %u display(s)\n", cDisplaysOut);
     408                drmSendHints(hDevice, aDisplaysOut, cDisplaysOut);
     409            }
     410            else
     411            {
     412                VBClLogError("VBoxDRMClient: displays layout is invalid, will not notify guest driver, rc=%Rrc\n", rc);
     413            }
     414        }
     415
    341416        do
    342417        {
     
    346421            VBClLogFatalError("Failure waiting for event, rc=%Rrc\n", rc);
    347422    }
     423}
     424
     425int main(int argc, char *argv[])
     426{
     427    RTFILE hDevice = NIL_RTFILE;
     428    RTFILE hPidFile;
     429
     430    int rc = RTR3InitExe(argc, &argv, 0);
     431    if (RT_FAILURE(rc))
     432        return RTMsgInitFailure(rc);
     433
     434    rc = VbglR3InitUser();
     435    if (RT_FAILURE(rc))
     436        VBClLogFatalError("VBoxDRMClient: VbglR3InitUser failed: %Rrc", rc);
     437
     438
     439
     440    rc = VBClLogCreate("");
     441    if (RT_FAILURE(rc))
     442        VBClLogFatalError("VBoxDRMClient: failed to setup logging, rc=%Rrc\n", rc);
     443
     444    PRTLOGGER pReleaseLog = RTLogRelGetDefaultInstance();
     445    if (pReleaseLog)
     446    {
     447        rc = RTLogDestinations(pReleaseLog, "stdout");
     448        if (RT_FAILURE(rc))
     449            VBClLogFatalError("VBoxDRMClient: failed to redirert error output, rc=%Rrc", rc);
     450    }
     451    else
     452    {
     453        VBClLogFatalError("VBoxDRMClient: failed to get logger instance");
     454    }
     455
     456    /* Check PID file before attempting to initialize anything. */
     457    rc = VbglR3PidFile(g_szPidFile, &hPidFile);
     458    if (rc == VERR_FILE_LOCK_VIOLATION)
     459    {
     460        VBClLogInfo("VBoxDRMClient: already running, exiting\n");
     461        return RTEXITCODE_SUCCESS;
     462    }
     463    else if (RT_FAILURE(rc))
     464    {
     465        VBClLogError("VBoxDRMClient: unable to lock PID file (%Rrc), exiting\n", rc);
     466        return RTEXITCODE_FAILURE;
     467    }
     468
     469    hDevice = drmOpenVmwgfx();
     470    if (hDevice == NIL_RTFILE)
     471        return VERR_OPEN_FAILED;
     472
     473    rc = VbglR3CtlFilterMask(VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST, 0);
     474    if (RT_FAILURE(rc))
     475    {
     476        VBClLogFatalError("Failed to request display change events, rc=%Rrc\n", rc);
     477        return VERR_INVALID_HANDLE;
     478    }
     479    rc = VbglR3AcquireGuestCaps(VMMDEV_GUEST_SUPPORTS_GRAPHICS, 0, false);
     480    if (rc == VERR_RESOURCE_BUSY)  /* Someone else has already acquired it. */
     481    {
     482        return VERR_RESOURCE_BUSY;
     483    }
     484    if (RT_FAILURE(rc))
     485    {
     486        VBClLogFatalError("Failed to register resizing support, rc=%Rrc\n", rc);
     487        return VERR_INVALID_HANDLE;
     488    }
     489
     490    drmMainLoop(hDevice);
    348491
    349492    /** @todo this code never executed since we do not have yet a clean way to exit
     
    353496
    354497    VBClLogInfo("VBoxDRMClient: releasing PID file lock\n");
    355     VbglR3ClosePidFile(szPidFile, hPidFile);
     498    VbglR3ClosePidFile(g_szPidFile, hPidFile);
    356499
    357500    return 0;
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette