VirtualBox中HGCM通信机制分析(SharedFolder)
HGCM全称Host Guest Communication Manager。virtualbox暴露出一个虚拟pci设备VMMDev给客户机,用作宿主机与客户机之间进行数据通信的通道。
HGCM的一个重要的使用场景在共享文件夹功能,因此下文中以共享文件夹(SharedFolder)为例,通过数据的流向来分析hgcm通信过程。
共享文件夹功能简要的分为以下几块:
- guest端文件驱动
- guest端HGCM模块
- host端HGCM模块
- host端文件服务模块
本文重点在分析HGCM的工作流程,即guest端HGCM模块、host端HGCM模块,而guest端文件驱动以及host端文件服务模块则简略而过
HGCM通信的发起者–guest端文件驱动
- 驱动入口初始化(以window客户端为例)
DriverEntry // Additions/WINNT/SharedFolders/driver/vbsf.c
vbsfInitMRxDispatch
// --snip--
VBoxMRxDispatch.MRxQueryDirectory = VBoxMRxQueryDirectory;
VBoxMRxDispatch.MRxQueryFileInfo = VBoxMRxQueryFileInfo;
// --snip--
- 以函数
VBoxMRxQueryFileInfo
为例,分析其调用hgcm的流程
VBoxMRxQueryFileInfo
// Additions/common/VBoxGuest/lib/VBoxGuestR0LibSharedFolders.c
VbglR0SfFsInfo( , , , SHFL_INFO_GET | SHFL_INFO_FILE, &cbHGCMBuffer, (PSHFLDIRINFO)pHGCMBuffer);
// 初始化请求相关信息,
// 请求类型为SHFL_FN_INFORMATION --> include/VBox/shflsvc.h, 在host端文件服务模块会根据该类型来处理请求
VBOX_INIT_CALL(&data.callInfo, INFORMATION, pClient);
// Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCM.cpp
// 执行对应的hgcm请求
VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data));
// 发送irp请求,类型为VBGL_IOCTL_HGCM_CALL,此处仍位于kernel部分
// PS,应用程序如VBoxService的自动挂载共享文件夹(VBoxServiceAutoMount.cpp),最终也会调用到类似代码(如VbglR3SharedFolderGetName)
VbglR0IdcCallRaw(&handle->IdcHandle, VBGL_IOCTL_HGCM_CALL(cbData), &pData->Hdr, cbData);
HGCM数据流–guest端HGCM模块
irp
请求VBGL_IOCTL_HGCM_CALL
最终进入到内核ioctl
中
// Additions/common/VBoxGuest/VBoxGuest.cpp
VGDrvCommonIoCtl
vgdrvIoCtl_HGCMCallWrapper
vgdrvIoCtl_HGCMCallInner
// 此处为关键函数,在这里初始化HGCM Call、发送HGCM Call、回写返回数据。
VbglR0HGCMInternalCall(pInfo, cbInfo, fFlags, vgdrvHgcmAsyncWaitCallback, pDevExt, cMillies);
- 在
VbglR0HGCMInternalCall
中通过写io
的方式,vm-exit
退出到host
端
// Additions/common/VBoxGuest/lib/VBoxGuestR0LibHGCMInternal.cpp
VbglR0HGCMInternalCall
vbglR0HGCMInternalPreprocessCall
// vboxguest端实现了一套简单的小内存管理机制,且VbglR0GRAlloc添加了一层VMMDevRequestHeader包头
// 具体详见Additions/common/VBoxGuest/lib/VBoxGuestR0LibGenericRequest.cpp
VbglR0GRAlloc
// 主要工作是将guest空间的虚拟地址转(VMMDevHGCMParmType_LinAddr)化为物理地址(VMMDevHGCMParmType_PageList)
vbglR0HGCMInternalInitCall
vbglR0HGCMInternalDoCall(pHGCMCall, pfnAsyncCallback, pvAsyncData, u32AsyncData, &fLeakIt);
rc = VbglR0GRPerform(&pHGCMCall->header.header);
// 对虚拟设备VMMDev执行IO调用,产生vm-exit事件,回到host
ASMOutU32(g_vbgldata.portVMMDev + VMMDEV_PORT_OFF_REQUEST, (uint32_t)PhysAddr);
// 若返回值为异步标志VINF_HGCM_ASYNC_EXECUTE,则执行异步的Callback
// fnAsyncCallback主要工作是等待pHGCMCall->header.fu32Flags被置位VBOX_HGCM_REQ_DONE,该标志是host端HGCM模块完成所有工作时写入
fnAsyncCallback(&pHGCMCall->header, pvAsyncData, u32AsyncData);
// 若标志VBOX_HGCM_REQ_DONE超时,则发送VMMDevReq_HGCMCancel2请求
VbglR0GRAlloc((VMMDevRequestHeader **)&pCancelReq, sizeof(*pCancelReq), VMMDevReq_HGCMCancel2);
vbglR0HGCMInternalCopyBackResult
至此HGCM的guest端的请求过程已完成,下一步即将到达host。
-
Guest虚拟PCI设备
VMMDev
客户机设备驱动中对虚拟设备对应
port
的io
读写,会被映射到virtualbox
注册的回调函数,在host端HGCM模块中会详细分析
// Additions/common/VBoxGuest/VBoxGuest.cpp
vgdrvNtSetupDevice
// scan pci的设备资源,这里VMMDev提供Port资源
// CmResourceTypePort、CmResourceTypeInterrupt、CmResourceTypeMemory、
vgdrvNtScanPCIResourceList
// 设备的物理地址映射为虚拟地址
vgdrvNtMapVMMDevMemory
VGDrvCommonInitDevExtResources(&pDevExt->Core,
pDevExt->Core.IOPortBase,
pvMMIOBase, cbMMIO,
vgdrvNtVersionToOSType(g_enmVGDrvNtVer),
VMMDEV_EVENT_MOUSE_POSITION_CHANGED);
VbglR0InitPrimary(pDevExt->IOPortBase, (VMMDevMemory *)pDevExt->pVMMDevMemory);
// 初始化小内存管理的链表头
vbglR0InitCommon()
// 初始化全局变量g_vbgldata,后续hgcm相关的地层调用通过该结构体进行
g_vbgldata.portVMMDev = portVMMDev;
g_vbgldata.pVMMDevMemory = pVMMDevMemory;
g_vbgldata.status = VbglStatusReady;
HGCM数据流–host端HGCM模块
host
端vmmdev设备初始化
// src/VBox/Devices/build/VBoxDD.cpp
VBoxDevicesRegister
// src/VBox/Devices/VMMDev/VMMDev.cpp
g_DeviceVMMDev
// src/VBox/Devices/VMMDev/VMMDevHGCM.cpp
vmmdevConstruct
// vmmdev一起注册了IO、MEM、
PDMDevHlpPCIIORegionRegister(pDevIns, 0, 0x20, PCI_ADDRESS_SPACE_IO, vmmdevIOPortRegionMap);
// src/VBox/Devices/VMMDev/VMMDev.cpp
vmmdevIOPortRegionMap
// hgcm是vmmdev request众多功能之一,具体见vmmdevReqDispatcher中的case项
PDMDevHlpIOPortRegister(xxx, GCPhysAddress + VMMDEV_PORT_OFF_REQUEST, 1,xxx, vmmdevRequestHandler, NULL, NULL, NULL, "VMMDev Request Handler");
// src/VBox/Devices/VMMDev/VMMDev.cpp
vmmdevRequestHandler
vmmdevReqHandler_HGCMConnect
vmmdevReqHandler_HGCMDisconnect
// 核心函数
vmmdevReqHandler_HGCMCall
// src/VBox/Devices/VMMDev/VMMDevHGCM.cpp
vmmdevHGCMCall
TODO:未完待续