diff --git a/repos/ports/lib/mk/virtualbox-devices.mk b/repos/ports/lib/mk/virtualbox-devices.mk
index f7df3c5d03..9e01d38868 100644
--- a/repos/ports/lib/mk/virtualbox-devices.mk
+++ b/repos/ports/lib/mk/virtualbox-devices.mk
@@ -52,6 +52,8 @@ SRC_CC += Devices/Input/UsbKbd.cpp
SRC_CC += Devices/build/VBoxDD.cpp
+SRC_CC += devxhci.cc
+
INC_DIR += $(VBOX_DIR)/Devices/build
INC_DIR += $(VBOX_DIR)/Devices/Bus
@@ -95,3 +97,4 @@ vboxssdt-cpuhotplug.hex: vbox-cpuhotplug.dsl
rm $@.tmp $@.pre
vpath %.dsl $(VBOX_DIR)/Devices/PC
+vpath %.cc $(REP_DIR)/src/virtualbox
diff --git a/repos/ports/ports/virtualbox.hash b/repos/ports/ports/virtualbox.hash
index a5610f8b41..66edb7a9bb 100644
--- a/repos/ports/ports/virtualbox.hash
+++ b/repos/ports/ports/virtualbox.hash
@@ -1 +1 @@
-794a16b709f2e4c34e27cc7f9bf15711c6d9e450
+a06860f27b9a7153e68eb9a0f2e12cf2fcdd1f41
diff --git a/repos/ports/src/virtualbox/devices.cc b/repos/ports/src/virtualbox/devices.cc
index 9a90838253..e62f9e81dc 100644
--- a/repos/ports/src/virtualbox/devices.cc
+++ b/repos/ports/src/virtualbox/devices.cc
@@ -57,6 +57,7 @@ extern "C" int VBoxDevicesRegister(PPDMDEVREGCB pCallbacks, uint32_t u32Version)
REGISTER(DeviceICHAC97);
REGISTER(DeviceICH6_HDA);
+ REGISTER(DeviceXHCI);
+
return VINF_SUCCESS;
}
-
diff --git a/repos/ports/src/virtualbox/devxhci.cc b/repos/ports/src/virtualbox/devxhci.cc
new file mode 100644
index 0000000000..d1cf602937
--- /dev/null
+++ b/repos/ports/src/virtualbox/devxhci.cc
@@ -0,0 +1,571 @@
+/*
+ * \brief NEC XHCI device frontend
+ * \author Josef Soentgen
+ * \author Sebastian Sumpf
+ * \date 2015-12-10
+ */
+
+/*
+ * Copyright (C) 2015 Genode Labs GmbH
+ *
+ * This file is distributed under the terms of the GNU General Public License
+ * version 2.
+ */
+
+/* Genode includes */
+#include
+#include
+#include
+
+/* qemu-usb includes */
+#include
+
+/* Virtualbox includes */
+#define LOG_GROUP LOG_GROUP_DEV_EHCI
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef IN_RING3
+# include
+# include
+# include
+# include
+#endif
+#include
+#include
+
+
+static bool const verbose_timer = false;
+
+
+/************************
+ ** xHCI device struct **
+ ************************/
+
+struct Timer_queue;
+struct Destruction_helper;
+
+
+struct XHCI
+{
+ /** The PCI device. */
+ PCIDEVICE PciDev;
+
+ /** Pointer to the device instance - R3 ptr. */
+ PPDMDEVINSR3 pDevInsR3;
+
+ /** Pointer to the device instance - R0 ptr */
+ PPDMDEVINSR0 pDevInsR0;
+
+ /** Pointer to the device instance - RC ptr. */
+ PPDMDEVINSRC pDevInsRC;
+
+ /** Address of the MMIO region assigned by PCI. */
+ RTGCPHYS32 MMIOBase;
+
+ /** Receiver thread that handles all USB signals. */
+ PPDMTHREAD pThread;
+
+ PTMTIMERR3 controller_timer;
+ Timer_queue *timer_queue;
+ Qemu::Controller *ctl;
+
+ Genode::Signal_receiver *usb_sig_rec;
+ Destruction_helper *destruction_helper;
+};
+
+
+/** 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
+{
+ struct Context : public Genode::List::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_list;
+ PTMTIMER tm_timer;
+
+ void _append_new_context(void *qtimer, void (*cb)(void*), void *data)
+ {
+ Context *new_ctx = new (Genode::env()->heap()) 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) {
+ PERR("qtimer: %p not found", qtimer);
+ throw -1;
+ }
+
+ if (c->pending) {
+ Context *min = _min_pending();
+ if (min == c) {
+ TMTimerStop(tm_timer);
+ _program_min_timer();
+ }
+ }
+
+ c->pending = false;
+ }
+
+ Timer_queue(PTMTIMER timer) : 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) {
+ Qemu::usb_timer_callback(c->cb, c->data);
+ c->pending = false;
+ }
+ }
+
+ _program_min_timer();
+ }
+
+ /**********************
+ ** TMTimer callback **
+ **********************/
+
+ static 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) PINF("timer: %p is pending", c);
+ res++;
+ }
+
+ return res;
+ }
+
+ /*********************************
+ ** Qemu::Timer_queue interface **
+ *********************************/
+
+ Qemu::int64_t get_ns() { return TMTimerGetNano(tm_timer); }
+
+ Genode::Lock _timer_lock;
+
+ void register_timer(void *qtimer, void (*cb)(void*), void *data) override
+ {
+ Genode::Lock::Guard lock_guard(_timer_lock);
+ if (verbose_timer)
+ PDBG("qtimer: %p cb: %p data: %p", qtimer, cb, data);
+
+ Context *c = _find_context(qtimer);
+ if (c != nullptr) {
+ PERR("qtimer: %p already registred", qtimer);
+ throw -1;
+ }
+
+ _append_new_context(qtimer, cb, data);
+ }
+
+ void delete_timer(void *qtimer) override
+ {
+ Genode::Lock::Guard lock_guard(_timer_lock);
+ if (verbose_timer)
+ PDBG("qtimer: %p", qtimer);
+
+ Context *c = _find_context(qtimer);
+ if (c == nullptr) {
+ PERR("qtimer: %p not found", qtimer);
+ throw -1;
+ }
+
+ _deactivate_timer(qtimer);
+
+ _context_list.remove(c);
+ Genode::destroy(Genode::env()->heap(), c);
+ }
+
+ void activate_timer(void *qtimer, long long int expire_abs) override
+ {
+ Genode::Lock::Guard lock_guard(_timer_lock);
+ if (verbose_timer)
+ PDBG("qtimer: %p expire: %lld", qtimer, expire_abs);
+
+ Context *c = _find_context(qtimer);
+ if (c == nullptr) {
+ PERR("qtimer: %p not found", qtimer);
+ throw -1;
+ }
+
+ c->timeout_abs_ns = expire_abs;
+ c->pending = true;
+
+ _program_min_timer();
+ }
+
+ void deactivate_timer(void *qtimer) override
+ {
+ Genode::Lock::Guard lock_guard(_timer_lock);
+ if (verbose_timer)
+ PDBG("qtimer: %p", qtimer);
+
+ _deactivate_timer(qtimer);
+ }
+};
+
+
+struct Pci_device : public Qemu::Pci_device
+{
+ PPDMDEVINS pci_dev;
+
+ Pci_device(PPDMDEVINS pDevIns) : pci_dev(pDevIns) { }
+
+ void raise_interrupt(int level) override {
+ PDMDevHlpPCISetIrqNoWait(pci_dev, 0, level); }
+
+ int read_dma(Qemu::addr_t addr, void *buf, size_t size) override {
+ return PDMDevHlpPhysRead(pci_dev, addr, buf, size); }
+
+ int write_dma(Qemu::addr_t addr, void const *buf, size_t size) override {
+ return PDMDevHlpPhysWrite(pci_dev, addr, buf, size); }
+
+ void *map_dma(Qemu::addr_t base, size_t size)
+ {
+ void *addr;
+ PGMR3PhysTlbGCPhys2Ptr(nullptr, base, true, &addr);
+ return addr;
+ }
+
+ void unmap_dma(void *addr, size_t size) { }
+};
+
+
+/*************************************
+ ** Qemu::Usb signal thread backend **
+ *************************************/
+
+struct Destruction_helper
+{
+ Genode::Signal_receiver &sig_rec;
+
+ Genode::Signal_dispatcher dispatcher {
+ sig_rec, *this, &Destruction_helper::handle };
+
+ void handle(unsigned) { }
+
+ Destruction_helper(Genode::Signal_receiver &sig_rec)
+ : sig_rec(sig_rec) { }
+};
+
+
+static DECLCALLBACK(int) usb_signal_thread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ PXHCI pThis = PDMINS_2_DATA(pDevIns, PXHCI);
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ Genode::Signal sig = pThis->usb_sig_rec->wait_for_signal();
+ int num = sig.num();
+
+ Genode::Signal_dispatcher_base *dispatcher;
+ dispatcher = dynamic_cast(sig.context());
+ if (dispatcher) {
+ dispatcher->dispatch(num);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static DECLCALLBACK(int) usb_signal_thread_wakeup(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ PXHCI pThis = PDMINS_2_DATA(pDevIns, PXHCI);
+
+ Genode::Signal_transmitter(pThis->destruction_helper->dispatcher).submit();
+
+ return VINF_SUCCESS;
+}
+
+
+/***********************************************
+ ** Virtualbox Device function implementation **
+ ***********************************************/
+
+/**
+ * @callback_method_impl{FNIOMMMIOREAD}
+ */
+PDMBOTHCBDECL(int) xhciMmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr,
+ void *pv, unsigned cb)
+{
+ PXHCI pThis = PDMINS_2_DATA(pDevIns, PXHCI);
+
+ Genode::off_t offset = GCPhysAddr - pThis->MMIOBase;
+ Qemu::Controller *ctl = pThis->ctl;
+
+ ctl->mmio_read(offset, pv, cb);
+ return 0;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIOWRITE}
+ */
+PDMBOTHCBDECL(int) xhciMmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr,
+ void const *pv, unsigned cb)
+{
+ PXHCI pThis = PDMINS_2_DATA(pDevIns, PXHCI);
+
+ Genode::off_t offset = GCPhysAddr - pThis->MMIOBase;
+ Qemu::Controller *ctl = pThis->ctl;
+
+ ctl->mmio_write(offset, pv, cb);
+ return 0;
+}
+
+
+/**
+ * @callback_method_impl{FNPCIIOREGIONMAP}
+ */
+static DECLCALLBACK(int) xhciR3Map(PPCIDEVICE pPciDev, int iRegion, RTGCPHYS GCPhysAddress,
+ uint32_t cb, PCIADDRESSSPACE enmType)
+{
+ PXHCI pThis = (PXHCI)pPciDev;
+ int rc = PDMDevHlpMMIORegister(pThis->CTX_SUFF(pDevIns), GCPhysAddress, cb, NULL /*pvUser*/,
+ IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_DWORD_ZEROED
+ | IOMMMIO_FLAGS_DBGSTOP_ON_COMPLICATED_WRITE,
+ xhciMmioWrite, xhciMmioRead, "USB XHCI");
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = PDMDevHlpMMIORegisterRC(pPciDev->pDevIns, GCPhysAddress, cb, NIL_RTRCPTR /*pvUser*/,
+ "xhciMmioWrite", "xhciMmioRead");
+ if (RT_FAILURE(rc))
+ return rc;
+
+ pThis->MMIOBase = GCPhysAddress;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) xhciReset(PPDMDEVINS pDevIns)
+{
+ PXHCI pThis = PDMINS_2_DATA(pDevIns, PXHCI);
+
+ Qemu::usb_reset();
+ Qemu::usb_update_devices();
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) xhciDestruct(PPDMDEVINS pDevIns)
+{
+ Qemu::usb_reset();
+ return 0;
+}
+
+
+/**
+ * @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_sig_rec = new (Genode::env()->heap()) Genode::Signal_receiver();
+ pThis->destruction_helper = new (Genode::env()->heap())
+ Destruction_helper(*(pThis->usb_sig_rec));
+
+ int rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, Timer_queue::tm_timer_cb,
+ pThis, TMTIMER_FLAGS_NO_CRIT_SECT,
+ "NEC-XHCI Timer", &pThis->controller_timer);
+
+ static Timer_queue timer_queue(pThis->controller_timer);
+ pThis->timer_queue = &timer_queue;
+ static Pci_device pci_device(pDevIns);
+
+ rc = PDMDevHlpThreadCreate(pDevIns, &pThis->pThread, pThis,
+ usb_signal_thread, usb_signal_thread_wakeup,
+ 32 * 1024 , RTTHREADTYPE_IO, "usb_signal");
+
+ pThis->ctl = Qemu::usb_init(timer_queue, pci_device, *pThis->usb_sig_rec);
+
+ /*
+ * Init instance data.
+ */
+ pThis->pDevInsR3 = pDevIns;
+ pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns);
+ pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+
+ PCIDevSetVendorId (&pThis->PciDev, 0x1033); /* PCI_VENDOR_ID_NEC */
+ PCIDevSetDeviceId (&pThis->PciDev, 0x0194); /* PCI_DEVICE_ID_NEC_UPD720200 */
+ PCIDevSetClassProg (&pThis->PciDev, 0x30); /* xHCI */
+ PCIDevSetClassSub (&pThis->PciDev, 0x03);
+ PCIDevSetClassBase (&pThis->PciDev, 0x0c);
+ PCIDevSetInterruptPin (&pThis->PciDev, 0x01);
+ PCIDevSetByte (&pThis->PciDev, 0x60, 0x30); /* Serial Bus Release Number Register */
+#ifdef VBOX_WITH_MSI_DEVICES
+ PCIDevSetStatus (&pThis->PciDev, VBOX_PCI_STATUS_CAP_LIST);
+ PCIDevSetCapabilityList(&pThis->PciDev, 0x80);
+#endif
+
+ /*
+ * Register PCI device and I/O region.
+ */
+ rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
+ if (RT_FAILURE(rc))
+ return rc;
+
+#ifdef VBOX_WITH_MSI_DEVICES
+ PDMMSIREG MsiReg;
+ RT_ZERO(MsiReg);
+ MsiReg.cMsiVectors = 1;
+ MsiReg.iMsiCapOffset = 0x80;
+ MsiReg.iMsiNextOffset = 0x00;
+ rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
+ if (RT_FAILURE(rc))
+ {
+ PCIDevSetCapabilityList(&pThis->PciDev, 0x0);
+ /* That's OK, we can work without MSI */
+ }
+#endif
+
+ rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, pThis->ctl->mmio_size(),
+ PCI_ADDRESS_SPACE_MEM, xhciR3Map);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ return VINF_SUCCESS;
+}
+
+const PDMDEVREG g_DeviceXHCI =
+{
+ /* u32version */
+ PDM_DEVREG_VERSION,
+ /* szName */
+ "nec-xhci",
+ /* szRCMod */
+ "VBoxDDGC.gc",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "NEC XHCI USB controller.\n",
+ /* fFlags */
+ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC,
+ /* fClass */
+ PDM_DEVREG_CLASS_BUS_USB,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(XHCI),
+ /* pfnConstruct */
+ xhciR3Construct,
+ /* pfnDestruct */
+ xhciDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnMemSetup */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ xhciReset,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnQueryInterface */
+ NULL,
+ /* pfnInitComplete */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32VersionEnd */
+ PDM_DEVREG_VERSION
+};
diff --git a/repos/ports/src/virtualbox/patches/series b/repos/ports/src/virtualbox/patches/series
index 3e55b211ff..c0ca6b11c5 100644
--- a/repos/ports/src/virtualbox/patches/series
+++ b/repos/ports/src/virtualbox/patches/series
@@ -24,3 +24,4 @@ tm_smp.patch
posix.patch
hostservice.patch
powerbutton.patch
+vbox_dd.patch
diff --git a/repos/ports/src/virtualbox/patches/vbox_dd.patch b/repos/ports/src/virtualbox/patches/vbox_dd.patch
new file mode 100644
index 0000000000..7043f28ff7
--- /dev/null
+++ b/repos/ports/src/virtualbox/patches/vbox_dd.patch
@@ -0,0 +1,10 @@
+--- a/src/app/virtualbox/src/VBox/Devices/build/VBoxDD.h
++++ b/src/app/virtualbox/src/VBox/Devices/build/VBoxDD.h
+@@ -58,6 +58,7 @@
+ extern const PDMDEVREG g_DeviceAudioSniffer;
+ extern const PDMDEVREG g_DeviceOHCI;
+ extern const PDMDEVREG g_DeviceEHCI;
++extern const PDMDEVREG g_DeviceXHCI;
+ extern const PDMDEVREG g_DeviceACPI;
+ extern const PDMDEVREG g_DeviceDMA;
+ extern const PDMDEVREG g_DeviceFloppyController;
diff --git a/repos/ports/src/virtualbox/patches/vbox_main.patch b/repos/ports/src/virtualbox/patches/vbox_main.patch
index e78f42160e..0a69372ac3 100644
--- a/repos/ports/src/virtualbox/patches/vbox_main.patch
+++ b/repos/ports/src/virtualbox/patches/vbox_main.patch
@@ -324,6 +324,17 @@ index b43f5a6..6aef9df 100644
////////////////////////////////////////////////////////////////////////////////
//
+--- a/src/app/virtualbox/src/VBox/Main/src-client/BusAssignmentManager.cpp
++++ b/src/app/virtualbox/src/VBox/Main/src-client/BusAssignmentManager.cpp
+@@ -72,6 +72,8 @@
+ {"usb-ohci", 0, 6, 0, 0},
+ {"usb-ehci", 0, 11, 0, 0},
+
++ {"nec-xhci", 0, 29, 0, 0},
++
+ /* ACPI controller */
+ {"acpi", 0, 7, 0, 0},
+
diff --git a/src/app/virtualbox/src/VBox/Main/src-client/ConsoleImpl.cpp b/src/app/virtualbox/src/VBox/Main/src-client/ConsoleImpl.cpp
index 69e3109..141bb98 100644
--- a/src/app/virtualbox/src/VBox/Main/src-client/ConsoleImpl.cpp
@@ -1064,6 +1075,21 @@ index caed4be..6d02496 100644
}
hrc = pMachine->COMGETTER(Name)(bstr.asOutParam()); H();
InsertConfigString(pCfg, "StreamName", bstr);
+@@ -2537,6 +2537,14 @@
+ #endif
+ } /* for every USB controller. */
+
++ /*
++ * NEC XHCI Device
++ */
++ InsertConfigNode(pDevices, "nec-xhci", &pDev);
++ InsertConfigNode(pDev, "0", &pInst);
++ InsertConfigNode(pInst, "Config", &pCfg);
++ InsertConfigInteger(pInst, "Trusted", 1);
++ hrc = pBusMgr->assignPCIDevice("nec-xhci", pInst); H();
+
+ /*
+ * Virtual USB Devices.
@@ -3533,6 +3549,8 @@ int Console::configMediumAttachment(PCFGMNODE pCtlInst,
ComPtr pMedium;
hrc = pMediumAtt->COMGETTER(Medium)(pMedium.asOutParam()); H();
diff --git a/repos/ports/src/virtualbox/target.mk b/repos/ports/src/virtualbox/target.mk
index 7ebb0e071a..e35c838843 100644
--- a/repos/ports/src/virtualbox/target.mk
+++ b/repos/ports/src/virtualbox/target.mk
@@ -26,6 +26,8 @@ LIBS += virtualbox-bios virtualbox-recompiler virtualbox-runtime \
LIBS += pthread libc_terminal libc_lock_pipe libiconv
+LIBS += qemu-usb
+
INC_DIR += $(call select_from_repositories,src/lib/libc)
INC_DIR += $(call select_from_repositories,src/lib/pthread)