vbox6: add QEMU xHCI device-model

This commit is contained in:
Josef Söntgen 2021-02-22 15:37:19 +01:00 committed by Christian Helmuth
parent 812c3599de
commit 1d551bd967
9 changed files with 650 additions and 1 deletions

View File

@ -107,6 +107,9 @@ SRC_CC += GuestHost/DragAndDrop/DnDDroppedFiles.cpp
SRC_CC += GuestHost/DragAndDrop/DnDMIME.cpp
SRC_CC += GuestHost/DragAndDrop/DnDPath.cpp
SRC_CC += devxhci.cc
INC_DIR += $(call select_from_repositories,src/lib/libc)
INC_DIR += $(VBOX_DIR)/Devices/build
INC_DIR += $(VBOX_DIR)/Devices/Bus
INC_DIR += $(VIRTUALBOX_DIR)/include/VBox/Graphics
@ -155,5 +158,6 @@ vboxssdt_cpuhotplug.hex: vbox-cpuhotplug.dsl
)
vpath %.dsl $(VBOX_DIR)/Devices/PC
vpath devxhci.cc $(REP_DIR)/src/virtualbox6
CC_CXX_WARN_STRICT =

View File

@ -1 +1 @@
53f753241f3d5253338f284aa833d7db04469026
e680f42ecc53c4f0507a6e832a3e48915a6c3b2a

View File

@ -63,5 +63,7 @@ extern "C" int VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version)
REGISTER(DeviceGIMDev);
REGISTER(DeviceLPC);
REGISTER(DeviceXHCI);
return VINF_SUCCESS;
}

View File

@ -0,0 +1,603 @@
/*
* \brief NEC XHCI device frontend
* \author Josef Soentgen
* \author Sebastian Sumpf
* \date 2015-12-10
*/
/*
* Copyright (C) 2015-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/log.h>
#include <base/attached_rom_dataspace.h>
#include <base/registry.h>
#include <libc/allocator.h>
#include <util/list.h>
/* qemu-usb includes */
#include <qemu/usb.h>
/* libc internal includes */
#include <internal/thread_create.h>
/* Virtualbox includes */
#define LOG_GROUP LOG_GROUP_DEV_EHCI
#include <VBox/pci.h>
#include <VBox/vmm/pdm.h>
#include <VBox/vmm/pgm.h>
#include <VBox/vmm/mm.h>
#include <VBox/err.h>
#include <VBox/log.h>
#include <iprt/assert.h>
#include <iprt/string.h>
#include <iprt/asm.h>
#include <iprt/asm-math.h>
#include <iprt/semaphore.h>
#include <iprt/critsect.h>
#ifdef IN_RING3
# include <iprt/alloca.h>
# include <iprt/mem.h>
# include <iprt/thread.h>
# include <iprt/uuid.h>
#endif
#include <VBox/vusb.h>
#include <VBoxDD.h>
/* local include */
#include "init.h"
static Genode::Env *_xhci_genode_env;
namespace Xhci {
void init(Genode::Env &env)
{
_xhci_genode_env = &env;
}
} /* namespace Xhci */
static bool const verbose_timer = false;
/************************
** xHCI device struct **
************************/
struct Timer_queue;
struct XHCI
{
/** Pointer to the device instance - R3 ptr. */
PPDMDEVINSR3 pDevInsR3;
/** The MMIO region handle. */
IOMMMIOHANDLE hMmio;
PTMTIMERR3 controller_timer;
Timer_queue *timer_queue;
Qemu::Controller *ctl;
Genode::Entrypoint *usb_ep;
};
/** Pointer to XHCI device data. */
typedef struct XHCI *PXHCI;
/** Read-only pointer to the XHCI device data. */
typedef struct XHCI const *PCXHCI;
/*************************************
** Qemu::Controller helper classes **
*************************************/
struct Timer_queue : public Qemu::Timer_queue
{
Genode::Allocator &_alloc;
struct Context : public Genode::List<Context>::Element
{
uint64_t timeout_abs_ns = ~0ULL;
bool pending = false;
void *qtimer = nullptr;
void (*cb)(void*) = nullptr;
void *data = nullptr;
Context(void *qtimer, void (*cb)(void*), void *data)
: qtimer(qtimer), cb(cb), data(data) { }
};
Genode::List<Context> _context_list;
PTMTIMER tm_timer;
void _append_new_context(void *qtimer, void (*cb)(void*), void *data)
{
Context *new_ctx = new (_alloc) Context(qtimer, cb, data);
_context_list.insert(new_ctx);
}
Context *_find_context(void const *qtimer)
{
for (Context *c = _context_list.first(); c; c = c->next())
if (c->qtimer == qtimer)
return c;
return nullptr;
}
Context *_min_pending()
{
Context *min = nullptr;
for (min = _context_list.first(); min; min = min->next())
if (min && min->pending)
break;
if (!min || !min->next())
return min;
for (Context *c = min->next(); c; c = c->next()) {
if ((c->timeout_abs_ns < min->timeout_abs_ns) && c->pending)
min = c;
}
return min;
}
void _program_min_timer()
{
Context *min = _min_pending();
if (min == nullptr) return;
if (TMTimerIsActive(tm_timer))
TMTimerStop(tm_timer);
TMTimerSetNano(tm_timer, min->timeout_abs_ns - TMTimerGetNano(tm_timer));
}
void _deactivate_timer(void *qtimer)
{
Context *c = _find_context(qtimer);
if (c == nullptr) {
Genode::error("qtimer: ", qtimer, " not found");
throw -1;
}
if (c == _min_pending()) {
c->pending = false;
TMTimerStop(tm_timer);
_program_min_timer();
}
c->pending = false;
}
Timer_queue(Genode::Allocator &alloc, PTMTIMER timer)
: _alloc(alloc), tm_timer(timer) { }
void timeout()
{
uint64_t now = TMTimerGetNano(tm_timer);
for (Context *c = _context_list.first(); c; c = c->next()) {
if (c->pending && c->timeout_abs_ns <= now) {
c->pending = false;
Qemu::usb_timer_callback(c->cb, c->data);
}
}
_program_min_timer();
}
/**********************
** TMTimer callback **
**********************/
static DECLCALLBACK(void) tm_timer_cb(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
PXHCI pThis = PDMINS_2_DATA(pDevIns, PXHCI);
Timer_queue *q = pThis->timer_queue;
q->timeout();
}
unsigned count_timer()
{
unsigned res = 0;
for (Context *c = _context_list.first(); c; c = c->next()) {
if (c->pending) Genode::log("timer: ", c, " is pending");
res++;
}
return res;
}
/*********************************
** Qemu::Timer_queue interface **
*********************************/
Qemu::int64_t get_ns() { return TMTimerGetNano(tm_timer); }
Genode::Mutex _timer_mutex { };
void register_timer(void *qtimer, void (*cb)(void*), void *data) override
{
Genode::Mutex::Guard guard(_timer_mutex);
if (verbose_timer)
Genode::log("qtimer: ", qtimer, " cb: ", cb, " data: ", data);
Context *c = _find_context(qtimer);
if (c != nullptr) {
Genode::error("qtimer: ", qtimer, " already registred");
throw -1;
}
_append_new_context(qtimer, cb, data);
}
void delete_timer(void *qtimer) override
{
Genode::Mutex::Guard guard(_timer_mutex);
if (verbose_timer)
Genode::log("qtimer: ", qtimer);
Context *c = _find_context(qtimer);
if (c == nullptr) {
Genode::error("qtimer: ", qtimer, " not found");
throw -1;
}
_deactivate_timer(qtimer);
_context_list.remove(c);
Genode::destroy(_alloc, c);
}
void activate_timer(void *qtimer, long long int expire_abs) override
{
Genode::Mutex::Guard guard(_timer_mutex);
if (verbose_timer)
Genode::log("qtimer: ", qtimer, " expire: ", expire_abs);
Context *c = _find_context(qtimer);
if (c == nullptr) {
Genode::error("qtimer: ", qtimer, " not found");
throw -1;
}
c->timeout_abs_ns = expire_abs;
c->pending = true;
_program_min_timer();
}
void deactivate_timer(void *qtimer) override
{
Genode::Mutex::Guard guard(_timer_mutex);
if (verbose_timer)
Genode::log("qtimer: ", qtimer);
_deactivate_timer(qtimer);
}
};
struct Pci_device : public Qemu::Pci_device
{
Genode::Allocator &_alloc;
PPDMDEVINS pci_dev;
struct Dma_bounce_buffer
{
Genode::Allocator &_alloc;
Qemu::addr_t const base;
Qemu::size_t const size;
void * const addr { _alloc.alloc(size) };
Dma_bounce_buffer(Genode::Allocator &alloc,
Qemu::addr_t base,
Qemu::size_t size)
: _alloc { alloc }, base { base }, size { size }
{ }
virtual ~Dma_bounce_buffer()
{
_alloc.free(addr, size);
}
};
using Reg_dma_buffer = Genode::Registered<Dma_bounce_buffer>;
Genode::Registry<Reg_dma_buffer> _dma_buffers { };
Pci_device(Genode::Allocator &alloc, PPDMDEVINS pDevIns)
: _alloc(alloc), pci_dev(pDevIns) { }
void raise_interrupt(int level) override {
PDMDevHlpPCISetIrqNoWait(pci_dev, 0, level); }
int read_dma(Qemu::addr_t addr, void *buf, Qemu::size_t size) override {
return PDMDevHlpPhysRead(pci_dev, addr, buf, size); }
int write_dma(Qemu::addr_t addr, void const *buf, Qemu::size_t size) override {
return PDMDevHlpPhysWrite(pci_dev, addr, buf, size); }
void *map_dma(Qemu::addr_t base, Qemu::size_t size,
Qemu::Pci_device::Dma_direction dir) override
{
Reg_dma_buffer *dma = nullptr;
try {
dma = new (_alloc) Reg_dma_buffer(_dma_buffers,
_alloc, base, size);
} catch (...) {
return nullptr;
}
/* copy data for write request to bounce buffer */
if (dir == Qemu::Pci_device::Dma_direction::OUT) {
(void)PDMDevHlpPhysRead(pci_dev, base, dma->addr, size);
}
return dma->addr;
}
void unmap_dma(void *addr, Qemu::size_t size,
Qemu::Pci_device::Dma_direction dir) override
{
_dma_buffers.for_each([&] (Reg_dma_buffer &dma) {
if (dma.addr != addr) {
return;
}
/* copy data for read request from bounce buffer */
if (dir == Qemu::Pci_device::Dma_direction::IN) {
(void)PDMDevHlpPhysWrite(pci_dev,
dma.base, dma.addr, dma.size);
}
Genode::destroy(_alloc, &dma);
});
}
};
/***********************************************
** Virtualbox Device function implementation **
***********************************************/
/**
* @callback_method_impl{FNIOMMMIOREAD}
*/
PDMBOTHCBDECL(VBOXSTRICTRC) xhciMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off,
void *pv, unsigned cb)
{
PXHCI pThis = PDMINS_2_DATA(pDevIns, PXHCI);
Genode::off_t offset = off;
Qemu::Controller *ctl = pThis->ctl;
ctl->mmio_read(offset, pv, cb);
return 0;
}
/**
* @callback_method_impl{FNIOMMMIOWRITE}
*/
PDMBOTHCBDECL(VBOXSTRICTRC) xhciMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off,
void const *pv, unsigned cb)
{
PXHCI pThis = PDMINS_2_DATA(pDevIns, PXHCI);
Genode::off_t offset = off;
Qemu::Controller *ctl = pThis->ctl;
ctl->mmio_write(offset, pv, cb);
return 0;
}
/**
* @interface_method_impl{PDMDEVREG,pfnReset}
*/
static DECLCALLBACK(void) xhciR3Reset(PPDMDEVINS pDevIns)
{
PXHCI pThis = PDMINS_2_DATA(pDevIns, PXHCI);
Qemu::usb_reset();
Qemu::usb_update_devices();
}
/**
* @nterface_method_impl{PDMDEVREG,pfnDestruct}
*/
static DECLCALLBACK(int) xhciR3Destruct(PPDMDEVINS pDevIns)
{
Qemu::usb_reset();
return 0;
}
struct Usb_ep : Genode::Entrypoint
{
pthread_t _pthread;
void _handle_pthread_registration()
{
Genode::Thread *myself = Genode::Thread::myself();
if (!myself || Libc::pthread_create(&_pthread, *myself, &myself)) {
Genode::error("USB passthough will not work - thread for "
"pthread registration invalid");
}
}
Genode::Signal_handler<Usb_ep> _pthread_reg_sigh;
enum { USB_EP_STACK = 32u << 10, };
Usb_ep(Genode::Env &env)
:
Entrypoint(env, USB_EP_STACK, "usb_ep", Genode::Affinity::Location()),
_pthread_reg_sigh(*this, *this, &Usb_ep::_handle_pthread_registration)
{
Genode::Signal_transmitter(_pthread_reg_sigh).submit();
}
};
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct,XHCI constructor}
*/
static DECLCALLBACK(int) xhciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
PXHCI pThis = PDMINS_2_DATA(pDevIns, PXHCI);
PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
pThis->usb_ep = new Usb_ep(*_xhci_genode_env);
static Libc::Allocator alloc;
int rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, Timer_queue::tm_timer_cb,
pThis, TMTIMER_FLAGS_NO_CRIT_SECT,
"XHCI Timer", &pThis->controller_timer);
static Timer_queue timer_queue(alloc, pThis->controller_timer);
pThis->timer_queue = &timer_queue;
static Pci_device pci_device(alloc, pDevIns);
pThis->ctl = Qemu::usb_init(timer_queue, pci_device, *pThis->usb_ep,
alloc, *_xhci_genode_env);
Qemu::Controller::Info const ctl_info = pThis->ctl->info();
/*
* Init instance data.
*/
pThis->pDevInsR3 = pDevIns;
PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
PCIDevSetVendorId (pPciDev, ctl_info.vendor_id);
PCIDevSetDeviceId (pPciDev, ctl_info.product_id);
PCIDevSetClassBase (pPciDev, 0x0c); /* PCI serial */
PCIDevSetClassSub (pPciDev, 0x03); /* USB */
PCIDevSetClassProg (pPciDev, 0x30); /* xHCI */
PCIDevSetInterruptPin (pPciDev, 0x01);
PCIDevSetByte (pPciDev, 0x60, 0x30); /* Serial Bus Release Number Register */
/*
* Register PCI device and I/O region.
*/
rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
if (RT_FAILURE(rc))
return rc;
uint32_t const mmio_flags = IOMMMIO_FLAGS_READ_DWORD
| IOMMMIO_FLAGS_WRITE_DWORD_ZEROED;
// | IOMMMIO_FLAGS_DBGSTOP_ON_COMPLICATED_WRITE;
rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 0, pThis->ctl->mmio_size(),
PCI_ADDRESS_SPACE_MEM,
xhciMmioWrite, xhciMmioRead, NULL,
mmio_flags,
"QEMU XHCI", &pThis->hMmio);
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
const PDMDEVREG g_DeviceXHCI =
{
/* .u32version = */ PDM_DEVREG_VERSION,
/* .uReserved0 = */ 0,
/* .szName = */ "qemu-xhci",
/* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
/* .fClass = */ PDM_DEVREG_CLASS_BUS_USB,
/* .cMaxInstances = */ ~0U,
/* .uSharedVersion = */ 42,
/* .cbInstanceShared = */ sizeof(XHCI),
/* .cbInstanceCC = */ 0, //sizeof(XHCICC),
/* .cbInstanceRC = */ 0,
/* .cMaxPciDevices = */ 1,
/* .cMaxMsixVectors = */ 0,
/* .pszDescription = */ "QEMU XHCI USB controller.\n",
#if defined(IN_RING3)
/* .pszRCMod = */ "VBoxDDRC.rc",
/* .pszR0Mod = */ "VBoxDDR0.r0",
/* .pfnConstruct = */ xhciR3Construct,
/* .pfnDestruct = */ xhciR3Destruct,
/* .pfnRelocate = */ NULL,
/* .pfnMemSetup = */ NULL,
/* .pfnPowerOn = */ NULL,
/* .pfnReset = */ xhciR3Reset,
/* .pfnSuspend = */ NULL,
/* .pfnResume = */ NULL,
/* .pfnAttach = */ NULL,
/* .pfnDetach = */ NULL,
/* .pfnQueryInterface = */ NULL,
/* .pfnInitComplete = */ NULL,
/* .pfnPowerOff = */ NULL,
/* .pfnSoftReset = */ NULL,
/* .pfnReserved0 = */ NULL,
/* .pfnReserved1 = */ NULL,
/* .pfnReserved2 = */ NULL,
/* .pfnReserved3 = */ NULL,
/* .pfnReserved4 = */ NULL,
/* .pfnReserved5 = */ NULL,
/* .pfnReserved6 = */ NULL,
/* .pfnReserved7 = */ NULL,
#elif defined(IN_RING0)
/* .pfnEarlyConstruct = */ NULL,
/* .pfnConstruct = */ NULL,
/* .pfnDestruct = */ NULL,
/* .pfnFinalDestruct = */ NULL,
/* .pfnRequest = */ NULL,
/* .pfnReserved0 = */ NULL,
/* .pfnReserved1 = */ NULL,
/* .pfnReserved2 = */ NULL,
/* .pfnReserved3 = */ NULL,
/* .pfnReserved4 = */ NULL,
/* .pfnReserved5 = */ NULL,
/* .pfnReserved6 = */ NULL,
/* .pfnReserved7 = */ NULL,
#elif defined(IN_RC)
/* .pfnConstruct = */ NULL,
/* .pfnReserved0 = */ NULL,
/* .pfnReserved1 = */ NULL,
/* .pfnReserved2 = */ NULL,
/* .pfnReserved3 = */ NULL,
/* .pfnReserved4 = */ NULL,
/* .pfnReserved5 = */ NULL,
/* .pfnReserved6 = */ NULL,
/* .pfnReserved7 = */ NULL,
#else
# error "Not in IN_RING3, IN_RING0 or IN_RC!"
#endif
/* .u32VersionEnd = */ PDM_DEVREG_VERSION
};
bool use_xhci_controller()
{
try {
Genode::Attached_rom_dataspace config(*_xhci_genode_env, "config");
return config.xml().attribute_value("xhci", false);
} catch (Genode::Rom_connection::Rom_connection_failed) {
return false;
}
}

View File

@ -21,4 +21,6 @@ namespace Sup { void init(Genode::Env &); }
namespace Network { void init(Genode::Env &); }
namespace Xhci { void init(Genode::Env &); }
#endif /* _INIT_H_ */

View File

@ -360,6 +360,7 @@ void Libc::Component::construct(Libc::Env &env)
}
Sup::init(env);
Xhci::init(env);
try {
static Main main(env);

View File

@ -0,0 +1,35 @@
--- a/src/virtualbox6/src/VBox/Main/src-client/BusAssignmentManager.cpp
+++ b/src/virtualbox6/src/VBox/Main/src-client/BusAssignmentManager.cpp
@@ -77,6 +77,7 @@ static const DeviceAssignmentRule aGenericRules[] =
{"usb-ohci", 0, 6, 0, 0},
{"usb-ehci", 0, 11, 0, 0},
{"usb-xhci", 0, 12, 0, 0},
+ {"qemu-xhci", 0, 29, 0, 0},
/* ACPI controller */
#if 0
diff --git a/src/VBox/Main/src-client/ConsoleImpl2.cpp b/src/VBox/Main/src-client/ConsoleImpl2.cpp
index 9e8e481..dbb4681 100644
--- a/src/virtualbox6/src/VBox/Main/src-client/ConsoleImpl2.cpp
+++ a/src/virtualbox6/src/VBox/Main/src-client/ConsoleImpl2.cpp
@@ -2135,6 +2135,20 @@ int Console::i_configConstructorInner(PUVM pUVM, PVM pVM, AutoWriteLock *pAlock)
}
/*
+ * QEMU xHCI controllers.
+ */
+ extern bool use_xhci_controller();
+ if (use_xhci_controller()) {
+
+ InsertConfigNode(pDevices, "qemu-xhci", &pDev);
+ InsertConfigNode(pDev, "0", &pInst);
+ InsertConfigNode(pInst, "Config", &pCfg);
+ InsertConfigInteger(pInst, "Trusted", 1);
+ hrc = pBusMgr->assignPCIDevice("qemu-xhci", pInst); H();
+ }
+
+
+ /*
* Storage controllers.
*/
com::SafeIfaceArray<IStorageController> ctrls;

View File

@ -1,2 +1,3 @@
serial.patch
drvtap.patch
qemu-xhci.patch

View File

@ -11,6 +11,7 @@ SRC_CC += network.cc
LIBS += base
LIBS += stdcxx
LIBS += libiconv
LIBS += qemu-usb
CC_OPT_main = -Wno-multistatement-macros
CC_OPT += -DProgress=ClientProgress