diff options
Diffstat (limited to 'src/VBox/VMM/VMMR3/PDMDriver.cpp')
| -rw-r--r-- | src/VBox/VMM/VMMR3/PDMDriver.cpp | 263 |
1 files changed, 261 insertions, 2 deletions
diff --git a/src/VBox/VMM/VMMR3/PDMDriver.cpp b/src/VBox/VMM/VMMR3/PDMDriver.cpp index 6187532d4..0ac45bddd 100644 --- a/src/VBox/VMM/VMMR3/PDMDriver.cpp +++ b/src/VBox/VMM/VMMR3/PDMDriver.cpp @@ -269,7 +269,9 @@ static DECLCALLBACK(int) pdmR3DrvRegister(PCPDMDRVREGCB pCallbacks, PCPDMDRVREG VERR_PDM_UNKNOWN_DRVREG_VERSION); AssertReturn(pReg->szName[0], VERR_PDM_INVALID_DRIVER_REGISTRATION); AssertMsgReturn(RTStrEnd(pReg->szName, sizeof(pReg->szName)), - (".*s\n", sizeof(pReg->szName), pReg->szName), + ("%.*s\n", sizeof(pReg->szName), pReg->szName), + VERR_PDM_INVALID_DRIVER_REGISTRATION); + AssertMsgReturn(pdmR3IsValidName(pReg->szName), ("%.*s\n", pReg->szName), VERR_PDM_INVALID_DRIVER_REGISTRATION); AssertMsgReturn( !(pReg->fFlags & PDM_DRVREG_FLAGS_R0) || ( pReg->szR0Mod[0] @@ -369,6 +371,250 @@ PPDMDRV pdmR3DrvLookup(PVM pVM, const char *pszName) /** + * Transforms the driver chain as it's being instantiated. + * + * Worker for pdmR3DrvInstantiate. + * + * @returns VBox status code. + * @param pVM The VM handle. + * @param pDrvAbove The driver above, NULL if top. + * @param pLun The LUN. + * @param ppNode The AttachedDriver node, replaced if any + * morphing took place. + */ +static int pdmR3DrvMaybeTransformChain(PVM pVM, PPDMDRVINS pDrvAbove, PPDMLUN pLun, PCFGMNODE *ppNode) +{ + /* + * The typical state of affairs is that there are no injections. + */ + PCFGMNODE pCurTrans = CFGMR3GetFirstChild(CFGMR3GetChild(CFGMR3GetRoot(pVM), "PDM/DriverTransformations")); + if (!pCurTrans) + return VINF_SUCCESS; + + /* + * Gather the attributes used in the matching process. + */ + const char *pszDevice = pLun->pDevIns->Internal.s.pDevR3->pReg->szName; + char szLun[32]; + RTStrPrintf(szLun, sizeof(szLun), "%u", pLun->iLun); + const char *pszAbove = pDrvAbove ? pDrvAbove->Internal.s.pDrv->pReg->szName : "<top>"; + char *pszThisDrv; + int rc = CFGMR3QueryStringAlloc(*ppNode, "Driver", &pszThisDrv); + AssertMsgRCReturn(rc, ("Query for string value of \"Driver\" -> %Rrc\n", rc), + rc == VERR_CFGM_VALUE_NOT_FOUND ? VERR_PDM_CFG_MISSING_DRIVER_NAME : rc); + + uint64_t uInjectTransformationAbove = 0; + if (pDrvAbove) + { + rc = CFGMR3QueryIntegerDef(CFGMR3GetParent(*ppNode), "InjectTransformationPtr", &uInjectTransformationAbove, 0); + AssertLogRelRCReturn(rc, rc); + } + + + /* + * Enumerate possible driver chain transformations. + */ + unsigned cTransformations = 0; + for (; pCurTrans != NULL; pCurTrans = CFGMR3GetNextChild(pCurTrans)) + { + char szCurTransNm[256]; + rc = CFGMR3GetName(pCurTrans, szCurTransNm, sizeof(szCurTransNm)); + AssertLogRelRCReturn(rc, rc); + + /* Match against the driver multi pattern. */ + char *pszMultiPat; + rc = CFGMR3QueryStringAllocDef(pCurTrans, "Driver", &pszMultiPat, "*"); + AssertLogRelRCReturn(rc, rc); + bool fMatch = RTStrSimplePatternMultiMatch(pszMultiPat, RTSTR_MAX, pszDevice, RTSTR_MAX, NULL); + MMR3HeapFree(pszMultiPat); + if (!fMatch) + continue; + + /* Match against the lun multi pattern. */ + rc = CFGMR3QueryStringAllocDef(pCurTrans, "LUN", &pszMultiPat, "*"); + AssertLogRelRCReturn(rc, rc); + fMatch = RTStrSimplePatternMultiMatch(pszMultiPat, RTSTR_MAX, szLun, RTSTR_MAX, NULL); + MMR3HeapFree(pszMultiPat); + if (!fMatch) + continue; + + /* Match against the below-driver multi pattern. */ + rc = CFGMR3QueryStringAllocDef(pCurTrans, "BelowDriver", &pszMultiPat, "*"); + AssertLogRelRCReturn(rc, rc); + fMatch = RTStrSimplePatternMultiMatch(pszMultiPat, RTSTR_MAX, pszAbove, RTSTR_MAX, NULL); + MMR3HeapFree(pszMultiPat); + if (!fMatch) + continue; + + /* Match against the above-driver multi pattern. */ + rc = CFGMR3QueryStringAlloc(pCurTrans, "AboveDriver", &pszMultiPat); + if (rc == VERR_CFGM_VALUE_NOT_FOUND) + rc = VINF_SUCCESS; + else + { + AssertLogRelRCReturn(rc, rc); + fMatch = RTStrSimplePatternMultiMatch(pszMultiPat, RTSTR_MAX, pszThisDrv, RTSTR_MAX, NULL); + MMR3HeapFree(pszMultiPat); + if (!fMatch) + continue; + if (uInjectTransformationAbove == (uintptr_t)pCurTrans) + continue; + } + + /* + * We've got a match! Now, what are we supposed to do? + */ + char szAction[16]; + rc = CFGMR3QueryStringDef(pCurTrans, "Action", szAction, sizeof(szAction), "inject"); + AssertLogRelRCReturn(rc, rc); + AssertLogRelMsgReturn( !strcmp(szAction, "inject") + || !strcmp(szAction, "mergeconfig") + || !strcmp(szAction, "remove") + || !strcmp(szAction, "removetree") + || !strcmp(szAction, "replace") + || !strcmp(szAction, "replacetree") + , + ("Action='%s', valid values are 'inject', 'mergeconfig', 'replace', 'replacetree', 'remove', 'removetree'.\n", szAction), + VERR_PDM_MISCONFIGURED_DRV_TRANSFORMATION); + LogRel(("Applying '%s' to '%s'::[%s]...'%s': %s\n", szCurTransNm, pszDevice, szLun, pszThisDrv, szAction)); + CFGMR3Dump(*ppNode); + CFGMR3Dump(pCurTrans); + + /* Get the attached driver to inject. */ + PCFGMNODE pTransAttDrv = NULL; + if (!strcmp(szAction, "inject") || !strcmp(szAction, "replace") || !strcmp(szAction, "replacetree")) + { + pTransAttDrv = CFGMR3GetChild(pCurTrans, "AttachedDriver"); + AssertLogRelMsgReturn(pTransAttDrv, + ("An %s transformation requires an AttachedDriver child node!\n", szAction), + VERR_PDM_MISCONFIGURED_DRV_TRANSFORMATION); + } + + + /* + * Remove the node. + */ + if (!strcmp(szAction, "remove") || !strcmp(szAction, "removetree")) + { + PCFGMNODE pBelowThis = CFGMR3GetChild(*ppNode, "AttachedDriver"); + if (!pBelowThis || !strcmp(szAction, "removetree")) + { + CFGMR3RemoveNode(*ppNode); + *ppNode = NULL; + } + else + { + PCFGMNODE pBelowThisCopy; + rc = CFGMR3DuplicateSubTree(pBelowThis, &pBelowThisCopy); + AssertLogRelRCReturn(rc, rc); + + rc = CFGMR3ReplaceSubTree(*ppNode, pBelowThisCopy); + if (RT_FAILURE(rc)) + { + CFGMR3RemoveNode(pBelowThis); + AssertLogRelReturn(("rc=%Rrc\n", rc), rc); + } + } + } + /* + * Replace the driver about to be instantiated. + */ + else if (!strcmp(szAction, "replace") || !strcmp(szAction, "replacetree")) + { + PCFGMNODE pTransCopy; + rc = CFGMR3DuplicateSubTree(pTransAttDrv, &pTransCopy); + AssertLogRelRCReturn(rc, rc); + + PCFGMNODE pBelowThis = CFGMR3GetChild(*ppNode, "AttachedDriver"); + if (!pBelowThis || !strcmp(szAction, "replacetree")) + rc = VINF_SUCCESS; + else + { + PCFGMNODE pBelowThisCopy; + rc = CFGMR3DuplicateSubTree(pBelowThis, &pBelowThisCopy); + if (RT_SUCCESS(rc)) + { + rc = CFGMR3InsertSubTree(pTransCopy, "AttachedDriver", pBelowThisCopy, NULL); + AssertLogRelRC(rc); + if (RT_FAILURE(rc)) + CFGMR3RemoveNode(pBelowThisCopy); + } + } + if (RT_SUCCESS(rc)) + rc = CFGMR3ReplaceSubTree(*ppNode, pTransCopy); + if (RT_FAILURE(rc)) + CFGMR3RemoveNode(pTransCopy); + } + /* + * Inject a driver before the driver about to be instantiated. + */ + else if (!strcmp(szAction, "inject")) + { + PCFGMNODE pTransCopy; + rc = CFGMR3DuplicateSubTree(pTransAttDrv, &pTransCopy); + AssertLogRelRCReturn(rc, rc); + + PCFGMNODE pThisCopy; + rc = CFGMR3DuplicateSubTree(*ppNode, &pThisCopy); + if (RT_SUCCESS(rc)) + { + rc = CFGMR3InsertSubTree(pTransCopy, "AttachedDriver", pThisCopy, NULL); + if (RT_SUCCESS(rc)) + { + rc = CFGMR3InsertInteger(pTransCopy, "InjectTransformationPtr", (uintptr_t)pCurTrans); + AssertLogRelRC(rc); + rc = CFGMR3InsertString(pTransCopy, "InjectTransformationNm", szCurTransNm); + AssertLogRelRC(rc); + if (RT_SUCCESS(rc)) + rc = CFGMR3ReplaceSubTree(*ppNode, pTransCopy); + } + else + { + AssertLogRelRC(rc); + CFGMR3RemoveNode(pThisCopy); + } + } + if (RT_FAILURE(rc)) + CFGMR3RemoveNode(pTransCopy); + } + /* + * Merge the Config node of the transformation with the one of the + * current driver. + */ + else if (!strcmp(szAction, "mergeconfig")) + { + PCFGMNODE pTransConfig = CFGMR3GetChild(pCurTrans, "Config"); + AssertLogRelReturn(pTransConfig, VERR_PDM_MISCONFIGURED_DRV_TRANSFORMATION); + + PCFGMNODE pDrvConfig = CFGMR3GetChild(*ppNode, "Config"); + if (*ppNode) + CFGMR3InsertNode(*ppNode, "Config", &pDrvConfig); + AssertLogRelReturn(pDrvConfig, VERR_PDM_CANNOT_TRANSFORM_REMOVED_DRIVER); + + rc = CFGMR3CopyTree(pDrvConfig, pTransConfig, CFGM_COPY_FLAGS_REPLACE_VALUES | CFGM_COPY_FLAGS_MERGE_KEYS); + AssertLogRelRCReturn(rc, rc); + } + else + AssertFailed(); + + cTransformations++; + if (*ppNode) + CFGMR3Dump(*ppNode); + else + LogRel(("The transformation removed the driver.\n")); + } + + /* + * Note what happened in the release log. + */ + if (cTransformations > 0) + LogRel(("Transformations done. Applied %u driver transformations.\n", cTransformations)); + + return rc; +} + + +/** * Instantiate a driver. * * @returns VBox status code, including informational statuses. @@ -386,6 +632,10 @@ PPDMDRV pdmR3DrvLookup(PVM pVM, const char *pszName) * * @remarks Recursive calls to this function is normal as the drivers will * attach to anything below them during the pfnContruct call. + * + * @todo Need to extend this interface a bit so that the driver + * transformation feature can attach drivers to unconfigured LUNs and + * at the end of chains. */ int pdmR3DrvInstantiate(PVM pVM, PCFGMNODE pNode, PPDMIBASE pBaseInterface, PPDMDRVINS pDrvAbove, PPDMLUN pLun, PPDMIBASE *ppBaseInterface) @@ -396,10 +646,19 @@ int pdmR3DrvInstantiate(PVM pVM, PCFGMNODE pNode, PPDMIBASE pBaseInterface, PPDM Assert(pBaseInterface->pfnQueryInterface(pBaseInterface, PDMIBASE_IID) == pBaseInterface); /* + * Do driver chain injections + */ + int rc = pdmR3DrvMaybeTransformChain(pVM, pDrvAbove, pLun, &pNode); + if (RT_FAILURE(rc)) + return rc; + if (!pNode) + return VERR_PDM_NO_ATTACHED_DRIVER; + + /* * Find the driver. */ char *pszName; - int rc = CFGMR3QueryStringAlloc(pNode, "Driver", &pszName); + rc = CFGMR3QueryStringAlloc(pNode, "Driver", &pszName); if (RT_SUCCESS(rc)) { PPDMDRV pDrv = pdmR3DrvLookup(pVM, pszName); |
