summaryrefslogtreecommitdiff
path: root/src/VBox/VMM/VMMR3/PDMDriver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/VMM/VMMR3/PDMDriver.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/PDMDriver.cpp263
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);