diff --git a/repos/libports/include/qemu/usb.h b/repos/libports/include/qemu/usb.h new file mode 100644 index 0000000000..6660002c0b --- /dev/null +++ b/repos/libports/include/qemu/usb.h @@ -0,0 +1,129 @@ +/* + * \brief Qemu USB controller interface + * \author Josef Soentgen + * \author Sebastian Sumpf + * \date 2015-12-14 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__QEMU__USB_H_ +#define _INCLUDE__QEMU__USB_H_ + +#include +#include + +namespace Qemu { + + typedef Genode::size_t size_t; + typedef Genode::off_t off_t; + typedef Genode::int64_t int64_t; + typedef Genode::addr_t addr_t; + + + /************************************ + ** Qemu library backend interface ** + ************************************/ + + /* + * The backend interface needs to be provided the user of the library. + */ + + /** + * Timer_queue class + * + * The library uses the Timer_queue internally to schedule timeouts. + */ + struct Timer_queue + { + virtual int64_t get_ns() = 0; + virtual void register_timer(void *qtimer, void (*cb)(void *data), void *data) = 0; + virtual void delete_timer(void *qtimer) = 0; + virtual void activate_timer(void *qtimer, long long int expires_abs) = 0; + virtual void deactivate_timer(void *qtimer) = 0; + }; + + /** + * Pci_device class + * + * The library uses the Pci_device to access physical DMA memory and to + * raise interrupts. + */ + struct Pci_device + { + /** + * Raise interrupt + * + * \param assert 1 == raise, 0 == deassert interrupt + */ + virtual void raise_interrupt(int assert) = 0; + virtual int read_dma(addr_t addr, void *buf, size_t size) = 0; + virtual int write_dma(addr_t addr, void const *buf, size_t size) = 0; + virtual void *map_dma(addr_t base, size_t size) = 0; + virtual void unmap_dma(void *addr, size_t size) = 0; + }; + + + /************************************* + ** Qemu library frontend functions ** + *************************************/ + + /** + * Controller class to access MMIO register of the xHCI device model + * + * This class is implemented by the library. + */ + struct Controller + { + /** + * Size of the MMIO region of the controller + */ + virtual size_t mmio_size() const = 0; + + /** + * Read/write MMIO offset in the MMIO region of the controller + */ + virtual int mmio_read(off_t offset, void *buf, size_t size) = 0; + virtual int mmio_write(off_t offset, void const *buf, size_t size) = 0; + }; + + /** + * Initialize USB library + * + * \param tq Timer_queue instance provided by the user of the library + * \param pd Pci_device instance provided by the user of the library + * \param sr Signal_receiver used by the library to install signals + * + * \return Pointer to Controller object that is used to access the xHCI device state + */ + Controller *usb_init(Timer_queue &tq, Pci_device &pd, Genode::Signal_receiver &sr); + + /** + * Reset USB libray + */ + void usb_reset(); + + /** + * Update USB devices list + * + * Needs to be called after a library reset. + */ + void usb_update_devices(); + + /** + * Library timer callback + * + * Needs to be called when a timer triggers (see Timer_queue interface). + * + * \param cb Callback installed by Timer_queue::register_timer() + * \param data Data pointer given in Timer_queue::register_timer() + */ + void usb_timer_callback(void (*cb)(void *data), void *data); +} + +#endif /* _INCLUDE__QEMU__USB_H_ */ diff --git a/repos/libports/lib/import/import-qemu-usb_include.mk b/repos/libports/lib/import/import-qemu-usb_include.mk new file mode 100644 index 0000000000..a81e8e590d --- /dev/null +++ b/repos/libports/lib/import/import-qemu-usb_include.mk @@ -0,0 +1,11 @@ +QEMU_CONTRIB_DIR := $(call select_from_ports,qemu-usb)/src/lib/qemu + +LIB_DIR := $(REP_DIR)/src/lib/qemu-usb +LIB_INC_DIR := $(LIB_DIR)/include + +# +# The order of include-search directories is important, we need to look into +# 'contrib' before falling back to our custom 'qemu_emul.h' header. +INC_DIR += $(LIB_INC_DIR) +INC_DIR += $(QEMU_CONTRIB_DIR)/include +INC_DIR += $(LIB_CACHE_DIR)/qemu-usb_include/include diff --git a/repos/libports/lib/mk/qemu-usb.mk b/repos/libports/lib/mk/qemu-usb.mk new file mode 100644 index 0000000000..2acc67e5ca --- /dev/null +++ b/repos/libports/lib/mk/qemu-usb.mk @@ -0,0 +1,19 @@ +LIB_DIR = $(REP_DIR)/src/lib/qemu-usb +QEMU_USB_DIR = $(call select_from_ports,qemu-usb)/src/lib/qemu/hw/usb + +CC_WARN= + +INC_DIR += $(LIB_DIR) $(QEMU_USB_DIR) + +LIBS = qemu-usb_include + +SRC_CC = dummies.cc qemu_emul.cc host.cc + +SRC_C = hcd-xhci.c core.c bus.c + +SHARED_LIB = yes + +LD_OPT += --version-script=$(LIB_DIR)/symbol.map + +vpath %.c $(QEMU_USB_DIR) +vpath %.cc $(LIB_DIR) diff --git a/repos/libports/lib/mk/qemu-usb_include.mk b/repos/libports/lib/mk/qemu-usb_include.mk new file mode 100644 index 0000000000..ab3e0d1cd2 --- /dev/null +++ b/repos/libports/lib/mk/qemu-usb_include.mk @@ -0,0 +1,24 @@ +ifeq ($(called_from_lib_mk),yes) + +QEMU_CONTRIB_DIR := $(call select_from_ports,qemu-usb)/src/lib/qemu +QEMU_EMUL_H := $(REP_DIR)/src/lib/qemu-usb/include/qemu_emul.h + +# +# Determine the header files included by the contrib code. For each +# of these header files we create a symlink to 'qemu_emul.h'. +# +GEN_INCLUDES := $(shell grep -rh "^\#include .*" $(QEMU_CONTRIB_DIR) |\ + sed "s/^\#include [^<\"]*[<\"]\([^>\"]*\)[>\"].*/\1/" | sort | uniq) +# +# Put Qemu headers in 'GEN_INC' dir +# +GEN_INC := $(shell pwd)/include +GEN_INCLUDES := $(addprefix $(GEN_INC)/,$(GEN_INCLUDES)) + +all: $(GEN_INCLUDES) + +$(GEN_INCLUDES): + $(VERBOSE)mkdir -p $(dir $@) + $(VERBOSE)ln -s $(QEMU_EMUL_H) $@ + +endif diff --git a/repos/libports/ports/qemu-usb.hash b/repos/libports/ports/qemu-usb.hash new file mode 100644 index 0000000000..6ea380208d --- /dev/null +++ b/repos/libports/ports/qemu-usb.hash @@ -0,0 +1 @@ +cf9157628466843e495e600f15a40e64993dd87f diff --git a/repos/libports/ports/qemu-usb.port b/repos/libports/ports/qemu-usb.port new file mode 100644 index 0000000000..3604aa8a86 --- /dev/null +++ b/repos/libports/ports/qemu-usb.port @@ -0,0 +1,13 @@ +LICENSE := GPLv2 +VERSION := 2.4.1 +DOWNLOADS := qemu.archive + +URL(qemu) := http://wiki.qemu-project.org/download/qemu-$(VERSION).tar.bz2 +SHA(qemu) := 629fb77fc03713b1267c1d51a8df6c0d9c7fd39b +DIR(qemu) := src/lib/qemu +TAR_OPT(qemu) := --strip-components=1 --files-from $(REP_DIR)/src/lib/qemu-usb/files.list +HASH_INPUT += $(REP_DIR)/src/lib/qemu-usb/files.list + +PATCHES := src/lib/qemu-usb/patches/xhci_state.patch \ + src/lib/qemu-usb/patches/usb_bus_nfree.patch +PATCH_OPT:= -p1 diff --git a/repos/libports/src/lib/qemu-usb/README b/repos/libports/src/lib/qemu-usb/README new file mode 100644 index 0000000000..d37cba9811 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/README @@ -0,0 +1,30 @@ +This library makes the xHCI device model of Qemu available on Genode +and is used as a back end for such for device models in existing VMMs. + +Usage +~~~~~ + +The user of this library is required to provide certain back end +functionality, namely a Timer_queue to handle timer events and a Pci_device +that handles access to the PCI bus (raise interrupts, (un)map DMA memory) +within the VMM device model. + +To use this library the user calls 'Qemu::usb_init' and passes +pointers to the back end objects. In addition, a Signal_receiver +reference has also to be handed over. It will receive all signals +required by this library. + +'Qemu::usb_init' returns a pointer to a Controller object. MMIO +access must be forwarded to this object when the device model in the VMM +wants to access the MMIO regions of the xHCI device. + +Whenever the VMM requests a device reset the 'Qemu::usb_reset' +function has to be called. It will remove and free all attached +USB devices and will reset the state of the xHCI device model. + +After the xHCI device model has been reset 'Qemu::usb_update_devices' +needs to be called to reattach USB devices. + +Timer callbacks that have been registered using the Timer_queue +interface have to be executed by calling 'Qemu::usb_timer_callback' +when the timer triggers. diff --git a/repos/libports/src/lib/qemu-usb/dummies.cc b/repos/libports/src/lib/qemu-usb/dummies.cc new file mode 100644 index 0000000000..789b064515 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/dummies.cc @@ -0,0 +1,202 @@ +/* + * \brief Qemu USB controller interface + * \author Josef Soentgen + * \author Sebastian Sumpf + * \date 2015-12-14 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include +#include +#include + + +extern "C" { + +enum { + SHOW_TRACE = 0, +}; + +#define TRACE_AND_STOP \ + do { \ + PWRN("%s not implemented called from: %p", __func__, __builtin_return_address(0)); \ + Genode::sleep_forever(); \ + } while (0) + +#define TRACE \ + do { \ + if (SHOW_TRACE) \ + PWRN("%s not implemented", __func__); \ + } while (0) + + +/**************** + ** hcd-xhci.c ** + ****************/ + +void memory_region_del_subregion(MemoryRegion*, MemoryRegion*) +{ + TRACE_AND_STOP; +} + + +void msix_vector_unuse(PCIDevice*, unsigned int) +{ + TRACE; +} + + +int msix_vector_use(PCIDevice*, unsigned int) +{ + TRACE; + return 0; +} + + +ObjectClass* object_class_dynamic_cast_assert(ObjectClass*, const char*, const char*, int, + const char*) +{ + TRACE_AND_STOP; + return nullptr; +} + + +Object* object_dynamic_cast_assert(Object*, const char*, const char*, int, const char*) +{ + TRACE_AND_STOP; + return nullptr; +} + + +bool pci_bus_is_express(PCIBus*) +{ + TRACE; + return false; +} + + +void pci_register_bar(PCIDevice*, int, uint8_t, MemoryRegion*) +{ + TRACE; +} + + +int pcie_endpoint_cap_init(PCIDevice*, uint8_t) +{ + TRACE_AND_STOP; + return -1; +} + + +/*********** + ** bus.c ** + ***********/ + +static Error _error; +Error *error_abort = &_error; + + +ObjectClass* object_get_class(Object*) +{ + TRACE_AND_STOP; + return nullptr; +} + +const char* object_get_typename(Object*) +{ + TRACE; + return nullptr; +} + + +void qbus_set_bus_hotplug_handler(BusState *state, Error **error) +{ + TRACE; +} + + +gchar* g_strdup_printf(const gchar*, ...) +{ + TRACE; + return 0; +} + + +void pstrcpy(char*, int, const char*) +{ + TRACE; +} + + +long int strtol(const char*, char**, int) +{ + TRACE; + return -1; +} + + +DeviceState* qdev_try_create(BusState*, const char*) +{ + TRACE_AND_STOP; + return nullptr; +} + + +void monitor_printf(Monitor*, const char*, ...) +{ + TRACE; +} + + +void qdev_simple_device_unplug_cb(HotplugHandler*, DeviceState*, Error**) +{ + TRACE_AND_STOP; +} + + +char* qdev_get_dev_path(DeviceState*) +{ + TRACE_AND_STOP; + return 0; +} + + +const char* qdev_fw_name(DeviceState*) +{ + TRACE; + return 0; +} + + +gchar* g_strdup(const gchar*) +{ + TRACE; + return 0; +} + + +size_t strlen(const char*) +{ + TRACE_AND_STOP; + return 0; +} + + +/********** + ** libc ** + **********/ + +void abort() { TRACE_AND_STOP; } + +} /* extern "C" */ diff --git a/repos/libports/src/lib/qemu-usb/files.list b/repos/libports/src/lib/qemu-usb/files.list new file mode 100644 index 0000000000..f501ae6ec4 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/files.list @@ -0,0 +1,6 @@ +qemu-2.4.1/include/hw/usb.h +qemu-2.4.1/include/qemu/queue.h +qemu-2.4.1/include/qom/object.h +qemu-2.4.1/hw/usb/bus.c +qemu-2.4.1/hw/usb/core.c +qemu-2.4.1/hw/usb/hcd-xhci.c diff --git a/repos/libports/src/lib/qemu-usb/host.cc b/repos/libports/src/lib/qemu-usb/host.cc new file mode 100644 index 0000000000..e0ec56f711 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/host.cc @@ -0,0 +1,658 @@ +/** + * \brief USB session back end + * \author Josef Soentgen + * \author Sebastian Sumpf + * \date 2015-12-18 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +using namespace Genode; + +static bool const verbose_devices = false; +static bool const verbose_host = false; +Lock _lock; + + +static void update_ep(USBDevice *); +static bool claim_interfaces(USBDevice *dev); + + +struct Completion : Usb::Completion +{ + USBPacket *p = nullptr; + USBDevice *dev = nullptr; + uint8_t *data = nullptr; + enum State { VALID, FREE, CANCELED }; + State state = FREE; + + void complete(Usb::Packet_descriptor &p) override { } + + void complete(Usb::Packet_descriptor &packet, char *content) + { + if (state != VALID) + return; + + int actual_size = 0; + + switch (packet.type) { + case Usb::Packet_descriptor::CTRL: + actual_size = packet.control.actual_size; + break; + case Usb::Packet_descriptor::BULK: + case Usb::Packet_descriptor::IRQ: + actual_size = packet.transfer.actual_size; + default: + break; + } + + if (actual_size < 0) actual_size = 0; + + if (verbose_host) + PDBG("packet.type: %u actual_size: 0x%x", packet.type, actual_size); + + p->actual_length = 0; + + if (p->pid == USB_TOKEN_IN && actual_size > 0) { + if (data) Genode::memcpy(data, content, actual_size); + else usb_packet_copy(p, content, actual_size); + } + + p->actual_length = actual_size; + + if (packet.succeded) + p->status = USB_RET_SUCCESS; + else + p->status = USB_RET_IOERROR; + + switch (packet.type) { + case Usb::Packet_descriptor::CONFIG: + if (!claim_interfaces(dev)) + p->status = USB_RET_IOERROR; + case Usb::Packet_descriptor::ALT_SETTING: + update_ep(dev); + case Usb::Packet_descriptor::CTRL: + usb_generic_async_ctrl_complete(dev, p); + break; + case Usb::Packet_descriptor::BULK: + case Usb::Packet_descriptor::IRQ: + usb_packet_complete(dev, p); + break; + default: + break; + } + } +}; + + +struct Usb_host_device : List::Element +{ + struct Could_not_create_device : Genode::Exception { }; + + bool deleted = false; + char const *label = nullptr; + unsigned bus = 0; + unsigned dev = 0; + + USBHostDevice *qemu_dev; + Completion completion[Usb::Session::TX_QUEUE_SIZE]; + + Signal_receiver &sig_rec; + Signal_dispatcher state_dispatcher { sig_rec, *this, &Usb_host_device::state_change }; + + Allocator_avl alloc { env()->heap() }; + Usb::Connection usb_raw { &alloc, label, 1024*1024, state_dispatcher }; + + Signal_dispatcher ack_avail_dispatcher { sig_rec, *this, &Usb_host_device::ack_avail }; + + void _release_interfaces() + { + Usb::Config_descriptor cdescr; + Usb::Device_descriptor ddescr; + + usb_raw.config_descriptor(&ddescr, &cdescr); + + for (unsigned i = 0; i < cdescr.num_interfaces; i++) { + usb_raw.release_interface(i); + } + } + + bool _claim_interfaces() + { + Usb::Config_descriptor cdescr; + Usb::Device_descriptor ddescr; + + usb_raw.config_descriptor(&ddescr, &cdescr); + + bool result = true; + for (unsigned i = 0; i < cdescr.num_interfaces; i++) { + try { + usb_raw.claim_interface(i); + } catch (Usb::Session::Interface_already_claimed) { + result = false; + } + } + + if (!result) PERR("Device already claimed"); + + return result; + } + + Usb_host_device(Signal_receiver &sig_rec, char const *label, + unsigned bus, unsigned dev) + : label(label), bus(bus), dev(dev), sig_rec(sig_rec) + { + usb_raw.tx_channel()->sigh_ack_avail(ack_avail_dispatcher); + + if (!_claim_interfaces()) + throw Could_not_create_device(); + + qemu_dev = create_usbdevice(this); + + if (!qemu_dev) + throw Could_not_create_device(); + } + + static int to_qemu_speed(unsigned speed) + { + switch (speed) { + case Usb::Device::SPEED_LOW: return USB_SPEED_LOW; + case Usb::Device::SPEED_FULL: return USB_SPEED_FULL; + case Usb::Device::SPEED_HIGH: return USB_SPEED_HIGH; + case Usb::Device::SPEED_SUPER: return USB_SPEED_SUPER; + default: return 0; + } + } + + void ack_avail(unsigned) + { + Lock::Guard g(_lock); + + /* we are already dead, do nothing */ + if (deleted == true) return; + + while (usb_raw.source()->ack_avail()) { + Usb::Packet_descriptor packet = usb_raw.source()->get_acked_packet(); + + char *packet_content = usb_raw.source()->packet_content(packet); + dynamic_cast(packet.completion)->complete(packet, packet_content); + free_packet(packet); + } + } + + void _destroy() + { + /* mark delete before removing */ + deleted = true; + + /* remove from USB bus */ + remove_usbdevice(qemu_dev); + qemu_dev = nullptr; + } + + void state_change(unsigned) + { + Lock::Guard g(_lock); + if (usb_raw.plugged()) + return; + + _destroy(); + } + + void destroy() + { + Lock::Guard g(_lock); + + _release_interfaces(); + + _destroy(); + } + + Usb::Packet_descriptor alloc_packet(int length) + { + + if (!usb_raw.source()->ready_to_submit()) + throw -1; + + Usb::Packet_descriptor packet = usb_raw.source()->alloc_packet(length); + packet.completion = alloc_completion(); + return packet; + } + + void free_packet(Usb::Packet_descriptor &packet) + { + dynamic_cast(packet.completion)->state = Completion::FREE; + usb_raw.source()->release_packet(packet); + } + + Completion *alloc_completion() + { + for (unsigned i = 0; i < Usb::Session::TX_QUEUE_SIZE; i++) + if (completion[i].state == Completion::FREE) { + completion[i]. state = Completion::VALID; + return &completion[i]; + } + + return nullptr; + } + + Completion *find_completion(USBPacket *p) + { + for (unsigned i = 0; i < Usb::Session::TX_QUEUE_SIZE; i++) + if (completion[i].p == p) + return &completion[i]; + + return nullptr; + } + + void submit(Usb::Packet_descriptor p) { + usb_raw.source()->submit_packet(p); } + + bool claim_interfaces() { return _claim_interfaces(); } + + void set_configuration(uint8_t value, USBPacket *p) + { + _release_interfaces(); + + Usb::Packet_descriptor packet = alloc_packet(0); + packet.type = Usb::Packet_descriptor::CONFIG; + packet.number = value; + + Completion *c = dynamic_cast(packet.completion); + c->p = p; + c->dev = cast_USBDevice(qemu_dev); + submit(packet); + p->status = USB_RET_ASYNC; + } + + void set_interface(int index, uint8_t value, USBPacket *p) + { + Usb::Packet_descriptor packet = alloc_packet(0); + packet.type = Usb::Packet_descriptor::ALT_SETTING; + packet.interface.number = index; + packet.interface.alt_setting = value; + + Completion *c = dynamic_cast(packet.completion); + c->p = p; + c->dev = cast_USBDevice(qemu_dev); + submit(packet); + p->status = USB_RET_ASYNC; + } + + void update_ep(USBDevice *udev) + { + usb_ep_reset(udev); + + /* retrieve device speed */ + Usb::Config_descriptor cdescr; + Usb::Device_descriptor ddescr; + + usb_raw.config_descriptor(&ddescr, &cdescr); + + for (unsigned i = 0; i < cdescr.num_interfaces; i++) { + udev->altsetting[i] = usb_raw.alt_settings(i); + } + + for (unsigned i = 0; i < cdescr.num_interfaces; i++) { + for (int j = 0; j < udev->altsetting[i]; j++) { + Usb::Interface_descriptor iface; + usb_raw.interface_descriptor(i, j, &iface); + for (unsigned k = 0; k < iface.num_endpoints; k++) { + Usb::Endpoint_descriptor endp; + usb_raw.endpoint_descriptor(i, j, k, &endp); + + int const pid = (endp.address & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT; + int const ep = (endp.address & 0xf); + uint8_t const type = (endp.attributes & 0x3); + + usb_ep_set_max_packet_size(udev, pid, ep, endp.max_packet_size); + usb_ep_set_type(udev, pid, ep, type); + usb_ep_set_ifnum(udev, pid, ep, i); + usb_ep_set_halted(udev, pid, ep, 0); + } + } + } + } +}; + + +/******************** + ** Qemu interface ** + ********************/ + +#define TRACE_AND_STOP do { PDBG("not implemented"); } while(false) + +#define USB_HOST_DEVICE(obj) \ + OBJECT_CHECK(USBHostDevice, (obj), TYPE_USB_HOST_DEVICE) + + +static void update_ep(USBDevice *udev) +{ + USBHostDevice *d = USB_HOST_DEVICE(udev); + Usb_host_device *dev = (Usb_host_device *)d->data; + + dev->update_ep(udev); +} + + +static bool claim_interfaces(USBDevice *udev) +{ + USBHostDevice *d = USB_HOST_DEVICE(udev); + Usb_host_device *dev = (Usb_host_device *)d->data; + + return dev->claim_interfaces(); +} + + +static void usb_host_realize(USBDevice *udev, Error **errp) +{ + USBHostDevice *d = USB_HOST_DEVICE(udev); + Usb_host_device *dev = (Usb_host_device *)d->data; + + /* retrieve device speed */ + Usb::Config_descriptor cdescr; + Usb::Device_descriptor ddescr; + + dev->usb_raw.config_descriptor(&ddescr, &cdescr); + + if (verbose_host) + PDBG("set udev->speed to %d", Usb_host_device::to_qemu_speed(ddescr.speed)); + + udev->speed = Usb_host_device::to_qemu_speed(ddescr.speed); + udev->speedmask = (1 << udev->speed); + + udev->flags |= (1 << USB_DEV_FLAG_IS_HOST); + + dev->update_ep(udev); +} + + +static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p) +{ + USBHostDevice *d = USB_HOST_DEVICE(udev); + Usb_host_device *dev = (Usb_host_device *)d->data; + Completion *c = dev->find_completion(p); + c->state = Completion::CANCELED; +} + + +static void usb_host_handle_data(USBDevice *udev, USBPacket *p) +{ + USBHostDevice *d = USB_HOST_DEVICE(udev); + Usb_host_device *dev = (Usb_host_device *)d->data; + + size_t size = 0; + unsigned timeout = 0; + Usb::Packet_descriptor::Type type = Usb::Packet_descriptor::BULK; + + switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) { + case USB_ENDPOINT_XFER_BULK: + type = Usb::Packet_descriptor::BULK; + size = usb_packet_size(p); + break; + case USB_ENDPOINT_XFER_INT: + type = Usb::Packet_descriptor::IRQ; + size = p->iov.size; + + /* values match usb_drv */ + switch (udev->speed) { + case USB_SPEED_SUPER: + timeout = 1<<15; + break; + case USB_SPEED_HIGH: + timeout = 1<<13; + break; + case USB_SPEED_FULL: + case USB_SPEED_LOW: + timeout = 1<<7; + break; + default: break; + } + break; + default: + PERR("not supported data request"); + break; + } + + bool const in = p->pid == USB_TOKEN_IN; + + Usb::Packet_descriptor packet = dev->alloc_packet(size); + packet.type = type; + packet.transfer.ep = p->ep->nr | (in ? USB_DIR_IN : 0); + packet.transfer.timeout = timeout; + + if (!in) { + char * const content = dev->usb_raw.source()->packet_content(packet); + usb_packet_copy(p, content, size); + } + + Completion *c = dynamic_cast(packet.completion); + c->p = p; + c->dev = udev; + c->data = nullptr; + + dev->submit(packet); + p->status = USB_RET_ASYNC; +} + + +static void usb_host_handle_control(USBDevice *udev, USBPacket *p, + int request, int value, int index, + int length, uint8_t *data) +{ + USBHostDevice *d = USB_HOST_DEVICE(udev); + Usb_host_device *dev = (Usb_host_device *)d->data; + + if (verbose_host) + PDBG("r: %x v: %x i: %x length: %d", request, value, index, length); + + switch (request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + udev->addr = value; + return; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + dev->set_configuration(value & 0xff, p); + return; + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: + dev->set_interface(index, value, p); + return; + } + + if (udev->speed == USB_SPEED_SUPER && + !(udev->port->speedmask & USB_SPEED_MASK_SUPER) && + request == 0x8006 && value == 0x100 && index == 0) { + PERR("r->usb3ep0quirk = true"); + } + + Usb::Packet_descriptor packet; + try { + packet = dev->alloc_packet(length); + } catch (...) { PERR("Packet allocation failed"); return; } + + packet.type = Usb::Packet_descriptor::CTRL; + packet.control.request_type = request >> 8; + packet.control.request = request & 0xff; + packet.control.index = index; + packet.control.value = value; + + Completion *c = dynamic_cast(packet.completion); + c->p = p; + c->dev = udev; + c->data = data; + + if (!(packet.control.request_type & USB_DIR_IN) && length) { + char *packet_content = dev->usb_raw.source()->packet_content(packet); + Genode::memcpy(packet_content, data, length); + } + + dev->submit(packet); + p->status = USB_RET_ASYNC; +} + + +static Property usb_host_dev_properties[] = { + DEFINE_PROP_END_OF_LIST(), +}; + + +static void usb_host_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + USBDeviceClass *uc = USB_DEVICE_CLASS(klass); + + uc->realize = usb_host_realize; + uc->product_desc = "USB Host Device"; + uc->cancel_packet = usb_host_cancel_packet; + uc->handle_data = usb_host_handle_data; + uc->handle_control = usb_host_handle_control; + dc->props = usb_host_dev_properties; +} + + +static TypeInfo usb_host_dev_info; + + +static void usb_host_register_types(void) +{ + usb_host_dev_info.name = TYPE_USB_HOST_DEVICE; + usb_host_dev_info.parent = TYPE_USB_DEVICE; + usb_host_dev_info.instance_size = sizeof(USBHostDevice); + usb_host_dev_info.class_init = usb_host_class_initfn; + + type_register_static(&usb_host_dev_info); +} + + +struct Usb_devices : List +{ + Signal_receiver &_sig_rec; + Signal_dispatcher _device_dispatcher { _sig_rec, *this, &Usb_devices::_devices_update }; + Attached_rom_dataspace _devices_rom { "usb_devices" }; + + void _garbage_collect() + { + for (Usb_host_device *d = first(); d; d = d->next()) { + if (d->deleted == false) + continue; + + remove(d); + Genode::destroy(env()->heap(), d); + } + } + + Usb_host_device *_find_usb_device(unsigned bus, unsigned dev) + { + for (Usb_host_device *d = first(); d; d = d->next()) + if (d->bus == bus && d->dev == dev) + return d; + + return nullptr; + } + + void _devices_update(unsigned) + { + Lock::Guard g(_lock); + + _garbage_collect(); + + _devices_rom.update(); + if (!_devices_rom.is_valid()) + return; + + if (verbose_devices) + PINF("%s", _devices_rom.local_addr()); + + Xml_node devices_node(_devices_rom.local_addr(), _devices_rom.size()); + devices_node.for_each_sub_node("device", [&] (Xml_node const &node) { + + unsigned product = node.attribute_value("product_id", 0); + unsigned vendor = node.attribute_value("vendor_id", 0); + unsigned bus = node.attribute_value("bus", 0); + unsigned dev = node.attribute_value("dev", 0); + + Genode::String<128> label; + try { + node.attribute("label").value(&label); + } catch (Genode::Xml_attribute::Nonexistent_attribute) { + PERR("No label found for device %03x:%03x", bus, dev); + return; + } + + /* ignore if already created */ + if (_find_usb_device(bus, dev)) return; + + try { + Usb_host_device *new_device = new (env()->heap()) + Usb_host_device(_sig_rec, label.string(), bus, dev); + + insert(new_device); + + PINF("Attach USB device %03x:%03x (%x:%x)", + bus, dev, vendor, product); + + } catch (...) { + PERR("Could not attach USB device %03x:%03x (%x:%x)", + bus, dev, vendor, product); + } + }); + } + + Usb_devices(Signal_receiver *sig_rec) + : _sig_rec(*sig_rec) + { + _devices_rom.sigh(_device_dispatcher); + } + + void destroy() + { + for (Usb_host_device *d = first(); d; d = d->next()) + d->destroy(); + + _garbage_collect(); + } +}; + + +static Usb_devices *_devices; + + +extern "C" void usb_host_destroy() +{ + if (_devices == nullptr) return; + + _devices->destroy(); +} + + +extern "C" void usb_host_update_devices() +{ + if (_devices == nullptr) return; + + _devices->_devices_update(0); +} + + +/* + * Do not use type_init macro because of name mangling + */ +extern "C" void _type_init_usb_host_register_types(Genode::Signal_receiver *sig_rec) +{ + usb_host_register_types(); + + static Usb_devices devices(sig_rec); + _devices = &devices; +} diff --git a/repos/libports/src/lib/qemu-usb/include/extern_c_begin.h b/repos/libports/src/lib/qemu-usb/include/extern_c_begin.h new file mode 100644 index 0000000000..18f2694a9b --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/include/extern_c_begin.h @@ -0,0 +1,27 @@ +/* + * \brief Include before including Linux headers in C++ + * \author Christian Helmuth + * \date 2014-08-21 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#define extern_c_begin + +extern "C" { + +/* some warnings should only be switched of for Linux headers */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpointer-arith" +#pragma GCC diagnostic ignored "-Wsign-compare" + +/* deal with C++ keywords used for identifiers etc. */ +#define private private_ +#define class class_ +#define new new_ +#define typename typename_ diff --git a/repos/libports/src/lib/qemu-usb/include/extern_c_end.h b/repos/libports/src/lib/qemu-usb/include/extern_c_end.h new file mode 100644 index 0000000000..87094bc936 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/include/extern_c_end.h @@ -0,0 +1,21 @@ +/* + * \brief Include after including Linux headers in C++ + * \author Christian Helmuth + * \date 2014-08-21 + */ + +/* + * Copyright (C) 2014 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#undef typename +#undef new +#undef class +#undef private + +#pragma GCC diagnostic pop + +} /* extern "C" */ diff --git a/repos/libports/src/lib/qemu-usb/include/qemu_emul.h b/repos/libports/src/lib/qemu-usb/include/qemu_emul.h new file mode 100644 index 0000000000..c5104d5c19 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/include/qemu_emul.h @@ -0,0 +1,728 @@ +/* + * \brief Qemu emulation environment + * \author Josef Soentgen + * \author Sebastian Sumpf + * \date 2015-12-14 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#ifndef _INCLUDE__QEMU_EMUL_H_ +#define _INCLUDE__QEMU_EMUL_H_ + +#include +#include + +void q_printf(char const *, ...) __attribute__((format(printf, 1, 2))); +void q_vprintf(char const *, va_list); + +typedef genode_uint8_t uint8_t; +typedef genode_uint16_t uint16_t; +typedef genode_uint32_t uint32_t; +typedef genode_uint64_t uint64_t; + +typedef genode_int32_t int32_t; +typedef genode_int64_t int64_t; +typedef signed long ssize_t; + +#ifndef __cplusplus +typedef _Bool bool; +enum { false = 0, true = 1 }; +#endif +typedef __SIZE_TYPE__ size_t; +typedef unsigned long dma_addr_t; +typedef uint64_t hwaddr; + +typedef struct uint16List +{ + union { + uint16_t value; + uint64_t padding; + }; + struct uint16List *next; +} uint16List; + + +/********** + ** libc ** + **********/ + +enum { + EINVAL = 22, +}; + +void *malloc(size_t size); +void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void free(void *p); + +void abort(); + +#define fprintf(fd, fmt, ...) q_printf(fmt, ##__VA_ARGS__) +int snprintf(char *buf, size_t size, const char *fmt, ...); +int strcmp(const char *s1, const char *s2); +size_t strlen(char const *s); +long int strtol(const char *nptr, char **endptr, int base); +char *strchr(const char *s, int c); + + +/************* + ** defines ** + *************/ + +#define NULL (void *)0 +#define QEMU_SENTINEL + +#define le32_to_cpu(x) (x) +#define cpu_to_le32(x) (x) +#define cpu_to_le64(x) (x) + +#define le32_to_cpus(x) +#define le64_to_cpus(x) + +/** + * Forward declarations + */ + +typedef struct Monitor Monitor; +typedef struct QDict QDict; + +typedef struct CPUReadMemoryFunc CPUReadMemoryFunc; +typedef struct CPUWriteMemoryFunc CPUWriteMemoryFunc; + +typedef struct MemoryRegion { unsigned dummy; } MemoryRegion; + +struct tm; + + +/****************** + ** qapi/error.h ** + ******************/ + +typedef struct Error { char string[256]; } Error; + +void error_setg(Error **errp, const char *fmt, ...); + +const char *error_get_pretty(Error *err); +void error_report(const char *fmt, ...); +void error_free(Error *err); +void error_propagate(Error **dst_errp, Error *local_err); + +extern Error *error_abort; + + +/******************* + ** qemu/bitops.h ** + *******************/ + +#define BITS_PER_BYTE 8 +#define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE) + +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) + +#define DECLARE_BITMAP(name,bits) \ + unsigned long name[BITS_TO_LONGS(bits)] + +static inline void set_bit(long nr, unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long *p = addr + BIT_WORD(nr); + + *p |= mask; +} + + +/******************* + ** qemu-common.h ** + *******************/ + +struct iovec +{ + void *iov_base; + size_t iov_len; +}; + +typedef struct QEMUIOVector +{ + struct iovec *iov; + int niov; + size_t size; + int alloc_hint; +} QEMUIOVector; + +void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint); +void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); +void qemu_iovec_reset(QEMUIOVector *qiov); +void qemu_iovec_destroy(QEMUIOVector *qiov); + +void pstrcpy(char *buf, int buf_size, const char *str); + + +/**************** + ** qemu/iov.h ** + ****************/ + +size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, + size_t offset, const void *buf, size_t bytes); +size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, + size_t offset, void *buf, size_t bytes); +size_t iov_memset(const struct iovec *iov, const unsigned int iov_cnt, + size_t offset, int fillc, size_t bytes); + +/****************** + ** qom/object.h ** + ******************/ + +typedef struct Object { unsigned dummy; } Object; +typedef struct ObjectClass { unsigned dummy; } ObjectClass; + +struct DeviceState; +struct PCIDevice; +struct XHCIState; + +struct PCIDevice *cast_PCIDevice(void *); +struct XHCIState *cast_XHCIState(void *); +struct DeviceState *cast_DeviceState(void *); +struct BusState *cast_BusState(void *); +struct Object *cast_object(void *); +struct USBDevice *cast_USBDevice(void *); +struct USBHostDevice *cast_USBHostDevice(void *); + +struct PCIDeviceClass *cast_PCIDeviceClass(void *); +struct DeviceClass *cast_DeviceClass(void *); +struct BusClass *cast_BusClass(void *); +struct HotplugHandlerClass *cast_HotplugHandlerClass(void *); + +struct USBDeviceClass *cast_USBDeviceClass(void *); + +#define OBJECT_CHECK(type, obj, str) \ + cast_##type((void *)obj) + +#define OBJECT_CLASS_CHECK(type, klass, str) \ + OBJECT_CHECK(type, (klass), str) + +#define OBJECT(obj) \ + cast_object(obj) + +#define OBJECT_GET_CLASS(klass, obj, str) \ + OBJECT_CHECK(klass, obj, str) + + +typedef struct InterfaceInfo { + const char *type; +} InterfaceInfo; + + +typedef struct TypeInfo +{ + char const *name; + char const *parent; + size_t instance_size; + + bool abstract; + size_t class_size; + void (*class_init)(ObjectClass *klass, void *data); + InterfaceInfo *interfaces; +} TypeInfo; + +struct Type_impl { unsigned dummy; }; +typedef struct Type_impl *Type; + +Type type_register_static(const TypeInfo *info); + +#define object_property_set_bool(...) +#define object_unparent(...) + +const char *object_get_typename(Object *obj); + +/******************** + ** glib emulation ** + ********************/ + +#define g_new0(type, count)({ \ + typeof(type) *t = (typeof(type)*)malloc(sizeof(type) * count); \ + memset(t, 0, sizeof(type) * count); \ + t; \ + }) + +#define g_free(p) free(p) +#define g_malloc malloc + +#define g_malloc0(size) ({ \ + void *t = malloc((size)); \ + memset(t, 0, (size)); \ + t; \ + }) + +typedef void* gpointer; +typedef struct GSList GSList; + +struct GSList +{ + gpointer data; + GSList *next; +}; + +GSList *g_slist_append(GSList *list, gpointer data); + +typedef char gchar; +gchar *g_strdup(gchar const *str); +gchar *g_strdup_printf(gchar const *fmt, ...); + + +/******************** + ** hw/qdev-core.h ** + ********************/ + +typedef enum DeviceCategory { + DEVICE_CATEGORY_USB = 1, + DEVICE_CATEGORY_MAX +} DeviceCategory; + +typedef struct BusState BusState; + +typedef struct DeviceState +{ + BusState *parent_bus; +} DeviceState; + +struct VMStateDescription; +struct Property; + +typedef void (*DeviceRealize)(DeviceState *dev, Error **errp); +typedef void (*DeviceUnrealize)(DeviceState *dev, Error **errp); + +typedef struct DeviceClass +{ + DECLARE_BITMAP(categories, DEVICE_CATEGORY_MAX); + struct Property *props; + void (*reset)(DeviceState *dev); + DeviceRealize realize; + DeviceUnrealize unrealize; + + const struct VMStateDescription *vmsd; + const char *bus_type; +} DeviceClass; + +#define DEVICE_CLASS(klass) OBJECT_CHECK(DeviceClass, (klass), "device") + + +typedef struct BusState +{ + DeviceState *parent; + char *name; +} BusState; + +typedef struct BusClass +{ + void (*print_dev)(Monitor *mon, DeviceState *dev, int indent); + char *(*get_dev_path)(DeviceState *dev); + char *(*get_fw_dev_path)(DeviceState *dev); + +} BusClass; + +#define TYPE_BUS "bus" +#define BUS(obj) OBJECT_CHECK(BusState, (obj), TYPE_BUS) +#define BUS_CLASS(klass) OBJECT_CLASS_CHECK(BusClass, (klass), TYPE_BUS) + +#define TYPE_DEVICE "device" +#define DEVICE(obj) OBJECT_CHECK(DeviceState, (obj), TYPE_DEVICE) + +enum Prop_type +{ + BIT, UINT32, END +}; + +typedef struct Property +{ + enum Prop_type type; + unsigned offset; + unsigned long value; +} Property; + + +#define DEFINE_PROP_BIT(_name, _state, _field, _bit, _bool) \ +{ .type = BIT, .offset = offsetof(_state, _field), .value = (_bool << _bit) } +#define DEFINE_PROP_UINT32(_name, _state, _field, _value) \ +{ .type = UINT32, .offset = offsetof(_state, _field), .value = _value } +#define DEFINE_PROP_END_OF_LIST() { .type = END } +#define DEFINE_PROP_STRING(...) {} + + +/* forward */ +typedef struct DeviceState DeviceState; +typedef struct HotplugHandler HotplugHandler; + +void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); +void qbus_create_inplace(void *bus, size_t size, const char *typename, + DeviceState *parent, const char *name); +void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp); +DeviceState *qdev_create(BusState *bus, const char *name); +DeviceState *qdev_try_create(BusState *bus, const char *name); +char *qdev_get_dev_path(DeviceState *dev); +const char *qdev_fw_name(DeviceState *dev); + +/****************** + ** hw/hotplug.h ** + ******************/ + +typedef struct HotplugHandler { unsigned dummy; } HotplugHandler; + +typedef void (*hotplug_fn)(HotplugHandler *plug_handler, + DeviceState *plugged_dev, Error **errp); + +typedef struct HotplugHandlerClass +{ + hotplug_fn unplug; +} HotplugHandlerClass; + +#define TYPE_HOTPLUG_HANDLER "hotplug-handler" +#define HOTPLUG_HANDLER_CLASS(klass) \ + OBJECT_CLASS_CHECK(HotplugHandlerClass, (klass), TYPE_HOTPLUG_HANDLER) + + +/**************** + ** hw/osdep.h ** + ****************/ + +#define offsetof(type, member) ((size_t) &((type *)0)->member) + + +#define container_of(ptr, type, member) ({ \ + const typeof(((type *) 0)->member) *__mptr = (ptr); \ + (type *) ((char *) __mptr - offsetof(type, member));}) + +#define DO_UPCAST(type, field, dev) cast_DeviceStateToUSBBus() + +struct USBBus *cast_DeviceStateToUSBBus(void); + +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + + +/****************** + ** qemu/timer.h ** + ******************/ + +enum QEMUClockType +{ + QEMU_CLOCK_VIRTUAL = 1, +}; + +typedef enum QEMUClockType QEMUClockType; +typedef struct QEMUTimer { unsigned dummy; } QEMUTimer; +typedef void QEMUTimerCB(void *opaque); + +QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb, void *opaque); +void timer_mod(QEMUTimer *ts, int64_t expire_timer); +void timer_del(QEMUTimer *ts); +void timer_free(QEMUTimer *ts); + +int64_t qemu_clock_get_ns(QEMUClockType type); + + +/*********************** + ** exec/cpu-common.h ** + ***********************/ + +enum device_endian { DEVICE_LITTLE_ENDIAN = 2 }; + + +/******************* + ** exec/memory.h ** + *******************/ + +typedef struct AddressSpace { unsigned dummy; } AddressSpace; + +typedef struct MemoryRegionOps { + uint64_t (*read)(void *opaque, hwaddr addr, unsigned size); + void (*write)(void *opaque, hwaddr addr, uint64_t data, unsigned size); + + enum device_endian endianness; + + struct { + unsigned min_access_size; + unsigned max_access_size; + } valid; + + struct { + unsigned min_access_size; + unsigned max_access_size; + } impl; +} MemoryRegionOps; + +void memory_region_init(MemoryRegion *mr, + struct Object *owner, + const char *name, + uint64_t size); + +void memory_region_init_io(MemoryRegion *mr, + struct Object *owner, + const MemoryRegionOps *ops, + void *opaque, + const char *name, + uint64_t size); + +void memory_region_add_subregion(MemoryRegion *mr, + hwaddr offset, + MemoryRegion *subregion); + +void memory_region_del_subregion(MemoryRegion *mr, + MemoryRegion *subregion); + +/****************** + ** sysemu/dma.h ** + ******************/ + +typedef struct QEMUIOVector QEMUSGList; + +void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len); +void qemu_sglist_destroy(QEMUSGList *qsg); + +int dma_memory_read(AddressSpace *as, dma_addr_t addr, void *buf, dma_addr_t len); + +static inline uint64_t ldq_le_dma(AddressSpace *as, dma_addr_t addr) +{ + uint64_t val; + dma_memory_read(as, addr, &val, 8); + return val; +} + +static inline uint64_t ldq_le_pci_dma(void *dev, dma_addr_t addr) +{ + return ldq_le_dma(0, addr); +} + + +/************** + ** hw/pci.h ** + **************/ + +enum Pci_regs { + PCI_BASE_ADDRESS_SPACE_MEMORY = 0, + PCI_BASE_ADDRESS_MEM_TYPE_64 = 0x04, + PCI_CLASS_PROG = 0x09, + PCI_CACHE_LINE_SIZE = 0x0c, + PCI_INTERRUPT_PIN = 0x3d, +}; + +enum Pci_ids { + PCI_CLASS_SERIAL_USB = 0x0c03, + PCI_VENDOR_ID_NEC = 0x1033, + PCI_DEVICE_ID_NEC_UPD720200 = 0x0194, +}; + +typedef struct PCIBus { unsigned dummy; } PCIBus; + +typedef struct PCIDevice +{ + uint8_t config[0x1000]; /* PCIe config space */ + PCIBus *bus; + + uint8_t *msix_table; + uint8_t *msix_pba; + + MemoryRegion msix_table_mmio; + MemoryRegion msix_pba_mmio; + + unsigned *msix_entry_used; +} PCIDevice; + +typedef void PCIUnregisterFunc(PCIDevice *pci_dev); + +typedef struct PCIDeviceClass +{ + void (*realize)(PCIDevice *dev, Error **errp); + PCIUnregisterFunc *exit; + + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision; + uint16_t class_id; + + int is_express; + +} PCIDeviceClass; + +#define TYPE_PCI_DEVICE "pci-device" +#define PCI_DEVICE(obj) \ + OBJECT_CHECK(PCIDevice, (obj), TYPE_PCI_DEVICE) +#define PCI_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PCIDeviceClass, (klass), TYPE_PCI_DEVICE) + +int pci_dma_read(PCIDevice *dev, dma_addr_t addr, + void *buf, dma_addr_t len); + +int pci_dma_write(PCIDevice *dev, dma_addr_t addr, + const void *buf, dma_addr_t len); + +void pci_set_irq(PCIDevice *pci_dev, int level); +void pci_irq_assert(PCIDevice *pci_dev); +void pci_dma_sglist_init(QEMUSGList *qsg, PCIDevice *dev, + int alloc_hint); +AddressSpace *pci_get_address_space(PCIDevice *dev); + +void pci_register_bar(PCIDevice *pci_dev, int region_num, + uint8_t attr, MemoryRegion *memory); +bool pci_bus_is_express(PCIBus *bus); + +int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset); + + +/********************* + ** hw/pci/msi(x).h ** + *********************/ + +int msi_init(struct PCIDevice *dev, uint8_t offset, + unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask); + +int msix_init(PCIDevice *dev, unsigned short nentries, + MemoryRegion *table_bar, uint8_t table_bar_nr, + unsigned table_offset, MemoryRegion *pba_bar, + uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos); + +bool msi_enabled(const PCIDevice *dev); +int msix_enabled(PCIDevice *dev); + +int msix_vector_use(PCIDevice *dev, unsigned vector); +void msix_vector_unuse(PCIDevice *dev, unsigned vector); + +void msi_notify(PCIDevice *dev, unsigned int vector); +void msix_notify(PCIDevice *dev, unsigned vector); + + +/************************* + ** migration/vmstate.h ** + *************************/ + +#define VMSTATE_BOOL(...) {} +#define VMSTATE_UINT8(...) {} +#define VMSTATE_UINT32(...) {} +#define VMSTATE_UINT32_TEST(...) {} +#define VMSTATE_UINT64(...) {} +#define VMSTATE_INT32(...) {} +#define VMSTATE_INT64(...) {} +#define VMSTATE_STRUCT_ARRAY_TEST(...) {} +#define VMSTATE_UINT8_ARRAY(...) {} +#define VMSTATE_STRUCT_VARRAY_UINT32(...) {} +#define VMSTATE_PCIE_DEVICE(...) {} +#define VMSTATE_MSIX(...) {} +#define VMSTATE_TIMER_PTR(...) {} +#define VMSTATE_STRUCT(...) {} +#define VMSTATE_END_OF_LIST() {} + +typedef struct VMStateField { unsigned dummy; } VMStateField; +typedef struct VMStateDescription +{ + char const *name; + int version_id; + int minimum_version_id; + int (*post_load)(void *opaque, int version_id); + VMStateField *fields; +} VMStateDescription; + + +/************** + ** assert.h ** + **************/ + +#define assert(cond) do { \ + if (!(cond)) { \ + q_printf("assertion faied: %s:%d\n", __FILE__, __LINE__); \ + int *d = (int *)0x0; \ + *d = 1; \ + }} while (0) + + +/************ + ** traces ** + ************/ + +#define trace_usb_xhci_irq_msix_use(v) +#define trace_usb_xhci_irq_msix_unuse(v) +#define trace_usb_xhci_irq_msix(v) +#define trace_usb_xhci_irq_msi(v) +#define trace_usb_xhci_queue_event(...) +#define trace_usb_xhci_fetch_trb(...) +#define trace_usb_xhci_run(...) +#define trace_usb_xhci_stop(...) +#define trace_usb_xhci_ep_state(...) +#define trace_usb_xhci_ep_enable(...) +#define trace_usb_xhci_ep_disable(...) +#define trace_usb_xhci_ep_stop(...) +#define trace_usb_xhci_ep_reset(...) +#define trace_usb_xhci_ep_set_dequeue(...) +#define trace_usb_xhci_xfer_async(...) +#define trace_usb_xhci_xfer_nak(...) +#define trace_usb_xhci_xfer_success(...) +#define trace_usb_xhci_xfer_error(...) +#define trace_usb_xhci_xfer_start(...) +#define trace_usb_xhci_unimplemented(...) +#define trace_usb_xhci_ep_kick(...) +#define trace_usb_xhci_xfer_retry(...) +#define trace_usb_xhci_slot_enable(...) +#define trace_usb_xhci_slot_disable(...) +#define trace_usb_xhci_slot_address(...) +#define trace_usb_xhci_slot_configure(...) +#define trace_usb_xhci_irq_intx(...) +#define trace_usb_xhci_slot_reset(...) +#define trace_usb_xhci_slot_evaluate(...) +#define trace_usb_xhci_port_notify(...) +#define trace_usb_xhci_port_link(...) +#define trace_usb_xhci_port_reset(...) +#define trace_usb_xhci_reset(...) +#define trace_usb_xhci_cap_read(...) +#define trace_usb_xhci_port_read(...) +#define trace_usb_xhci_port_write(...) +#define trace_usb_xhci_oper_read(...) +#define trace_usb_xhci_oper_write(...) +#define trace_usb_xhci_runtime_read(...) +#define trace_usb_xhci_runtime_write(...) +#define trace_usb_xhci_doorbell_read(...) +#define trace_usb_xhci_doorbell_write(...) +#define trace_usb_xhci_exit(...) +#define trace_usb_port_claim(...) +#define trace_usb_port_release(...) +#define trace_usb_port_attach(...) +#define trace_usb_port_detach(...) +#define trace_usb_packet_state_fault(...) +#define trace_usb_packet_state_change(...) + +/*********************** + ** library interface ** + ***********************/ + +#define type_init(func) void _type_init_##func(void) { func(); } + +#define TYPE_USB_HOST_DEVICE "usb-host" + +typedef struct USBHostDevice +{ + void *data; +} USBHostDevice; + + +USBHostDevice *create_usbdevice(void *data); +void remove_usbdevice(USBHostDevice *device); + +void usb_host_destroy(); +void usb_host_update_devices(); + + +/*********************** + ** monitor/monitor.h ** + ***********************/ + +void monitor_printf(Monitor *mon, const char *fmt, ...); + + +#endif /* _INCLUDE__QEMU_EMUL_H_ */ diff --git a/repos/libports/src/lib/qemu-usb/patches/usb_bus_nfree.patch b/repos/libports/src/lib/qemu-usb/patches/usb_bus_nfree.patch new file mode 100644 index 0000000000..b7677728d0 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/patches/usb_bus_nfree.patch @@ -0,0 +1,19 @@ +diff --git a/src/lib/qemu/hw/usb/bus.c b/src/lib/qemu/hw/usb/bus.c +index 5f39e1e..b94f3af 100644 +--- a/src/lib/qemu/hw/usb/bus.c ++++ b/src/lib/qemu/hw/usb/bus.c +@@ -433,10 +433,10 @@ void usb_claim_port(USBDevice *dev, Error **errp) + return; + } + } else { +- if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) { +- /* Create a new hub and chain it on */ +- usb_try_create_simple(bus, "usb-hub", NULL); +- } ++ // if (bus->nfree == 1 && strcmp(object_get_typename(OBJECT(dev)), "usb-hub") != 0) { ++ // /* Create a new hub and chain it on */ ++ // usb_try_create_simple(bus, "usb-hub", NULL); ++ // } + if (bus->nfree == 0) { + error_setg(errp, "tried to attach usb device %s to a bus " + "with no free ports", dev->product_desc); diff --git a/repos/libports/src/lib/qemu-usb/patches/xhci_state.patch b/repos/libports/src/lib/qemu-usb/patches/xhci_state.patch new file mode 100644 index 0000000000..b9c57f61d7 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/patches/xhci_state.patch @@ -0,0 +1,27 @@ +diff --git a/src/lib/qemu/hw/usb/hcd-xhci.c b/src/lib/qemu/hw/usb/hcd-xhci.c +index c673bed..b2a8939 100644 +--- a/src/lib/qemu/hw/usb/hcd-xhci.c ++++ b/src/lib/qemu/hw/usb/hcd-xhci.c +@@ -486,6 +486,8 @@ struct XHCIState { + + #define TYPE_XHCI "nec-usb-xhci" + ++#ifndef __cplusplus ++ + #define XHCI(obj) \ + OBJECT_CHECK(XHCIState, (obj), TYPE_XHCI) + +@@ -2361,6 +2363,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, + + if (bsr) { + slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT; ++ usb_device_reset(dev); + } else { + USBPacket p; + uint8_t buf[1]; +@@ -3914,3 +3917,5 @@ static void xhci_register_types(void) + } + + type_init(xhci_register_types) ++ ++#endif /* __cplusplus */ diff --git a/repos/libports/src/lib/qemu-usb/qemu_emul.cc b/repos/libports/src/lib/qemu-usb/qemu_emul.cc new file mode 100644 index 0000000000..68549eab33 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/qemu_emul.cc @@ -0,0 +1,896 @@ +/* + * \brief Qemu USB controller interface + * \author Josef Soentgen + * \author Sebastian Sumpf + * \date 2015-12-14 + */ + +/* + * Copyright (C) 2015 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +/* Genode includes */ +#include +#include +#include + +/* local includes */ +#include +#include +/* XHCIState is defined in this file */ +#include +#include + +#include + +/******************* + ** USB interface ** + *******************/ + +static bool const verbose_irq = false; +static bool const verbose_iov = false; +static bool const verbose_mmio = false; + +extern "C" void _type_init_usb_register_types(); +extern "C" void _type_init_usb_host_register_types(Genode::Signal_receiver*); +extern "C" void _type_init_xhci_register_types(); + +extern Genode::Lock _lock; + +Qemu::Controller *qemu_controller(); + + +static Qemu::Timer_queue* _timer_queue; +static Qemu::Pci_device* _pci_device; + + +Qemu::Controller *Qemu::usb_init(Timer_queue &tq, Pci_device &pci, Genode::Signal_receiver &sig_rec) +{ + _timer_queue = &tq; + _pci_device = &pci; + + _type_init_usb_register_types(); + _type_init_xhci_register_types(); + _type_init_usb_host_register_types(&sig_rec); + + return qemu_controller(); +} + +void reset_controller(); + +void Qemu::usb_reset() +{ + usb_host_destroy(); + + reset_controller(); +} + + +void Qemu::usb_update_devices() { + usb_host_update_devices(); } + + +void Qemu::usb_timer_callback(void (*cb)(void*), void *data) +{ + Genode::Lock::Guard g(_lock); + + cb(data); +} + + +/********** + ** libc ** + **********/ + +void *malloc(size_t size) { + return Genode::env()->heap()->alloc(size); } + + +void *memset(void *s, int c, size_t n) { + return Genode::memset(s, c, n); } + + +void free(void *p) { + Genode::env()->heap()->free(p, 0); } + + +void q_printf(char const *fmt, ...) +{ + va_list va; + va_start(va, fmt); + Genode::vprintf(fmt, va); + va_end(va); +} + + +int snprintf(char *buf, size_t size, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + Genode::String_console sc(buf, size); + sc.vprintf(fmt, args); + va_end(args); + + return sc.len(); +} + + +int strcmp(const char *s1, const char *s2) +{ + return Genode::strcmp(s1, s2); +} + + +/******************** + ** Qemu emulation ** + ********************/ + +/** + * Set properties of objects + */ +template +void properties_apply(T *state, DeviceClass *klass) +{ + Property *p = klass->props; + + if (!p) + return; + + while (p->type != END) { + char *s = (char *)state; + s += p->offset; + uint32_t *member = (uint32_t *)s; + if (p->type == BIT) + *member |= p->value; + if (p->type == UINT32) + *member = p->value; + + p++; + } +} + + +/** + * Class and objects wrapper + */ +struct Wrapper +{ + unsigned long _start; + Object _object; + DeviceState _device_state; + PCIDevice _pci_device; + USBDevice _usb_device; + BusState _bus_state; + XHCIState *_xhci_state = nullptr; + USBHostDevice _usb_host_device; + + ObjectClass _object_class; + DeviceClass _device_class; + PCIDeviceClass _pci_device_class; + BusClass _bus_class; + USBDeviceClass _usb_device_class; + HotplugHandlerClass _hotplug_handler_class; + unsigned long _end; + + Wrapper() { } + + bool in_object(void * ptr) + { + /* + * XHCIState is roughly 3 MiB large so we only create on instance + * (see below) and have to do this pointer check shenanigans. + */ + if (_xhci_state != nullptr + && ptr >= _xhci_state + && ptr < ((char*)_xhci_state + sizeof(XHCIState))) + return true; + + using addr_t = Genode::addr_t; + + addr_t p = (addr_t)ptr; + + if (p > (addr_t)&_start && p < (addr_t)&_end) + return true; + + return false; + } +}; + + +struct Object_pool +{ + enum { + XHCI, /* XHCI controller */ + USB_BUS, /* bus driver */ + USB_DEVICE, /* USB device driver */ + USB_HOST_DEVICE, /* USB host device driver */ + MAX = 8 /* host devices (USB_HOST_DEVICE - MAX) */ + }; + + bool used[MAX]; + Wrapper obj[MAX]; + + Wrapper *create_object() + { + for (unsigned i = USB_HOST_DEVICE + 1; i < MAX; i++) { + if (used[i] == false) { + used[i] = true; + return &obj[i]; + } + } + return nullptr; + } + + void free_object(Wrapper *w) + { + for (unsigned i = USB_HOST_DEVICE + 1; i < MAX; i++) + if (w == &obj[i]) { + used[i] = false; + break; + } + } + + Wrapper * find_object(void *ptr) + { + for (Wrapper &w : obj) { + if(w.in_object(ptr)) + return &w; + } + + PERR("object for pointer not found called from: %p", __builtin_return_address(0)); + throw -1; + } + + XHCIState *xhci_state() { + return obj[XHCI]._xhci_state; } + + BusState *bus() { + return &obj[USB_BUS]._bus_state; } + + static Object_pool *p() + { + static Object_pool _p; + return &_p; + } +}; + + +/*********** + ** casts ** + ***********/ + +struct PCIDevice *cast_PCIDevice(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_pci_device; } + + +struct XHCIState *cast_XHCIState(void *ptr) { + return Object_pool::p()->find_object(ptr)->_xhci_state; } + + +struct DeviceState *cast_DeviceState(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_device_state; } + + +struct BusState *cast_BusState(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_bus_state; } + + +struct USBDevice *cast_USBDevice(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_usb_device; } + + +struct Object *cast_object(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_object; } + + +struct PCIDeviceClass *cast_PCIDeviceClass(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_pci_device_class; } + + +struct DeviceClass *cast_DeviceClass(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_device_class; } + + +struct USBDeviceClass *cast_USBDeviceClass(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_usb_device_class; } + + +struct BusClass *cast_BusClass(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_bus_class; } + + +struct HotplugHandlerClass *cast_HotplugHandlerClass(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_hotplug_handler_class; } + + +struct USBHostDevice *cast_USBHostDevice(void *ptr) { + return &Object_pool::p()->find_object(ptr)->_usb_host_device; } + + +struct USBBus *cast_DeviceStateToUSBBus(void) { + return &Object_pool::p()->xhci_state()->bus; } + + +USBHostDevice *create_usbdevice(void *data) +{ + Wrapper *obj = Object_pool::p()->create_object(); + if (!obj) { + PERR("could not create new object"); + return nullptr; + } + + obj->_usb_device_class = Object_pool::p()->obj[Object_pool::USB_HOST_DEVICE]._usb_device_class; + + /* + * Set parent bus object + */ + DeviceState *dev_state = &obj->_device_state; + dev_state->parent_bus = Object_pool::p()->bus(); + + /* set data pointer */ + obj->_usb_host_device.data = data; + + /* + * Attach new USB host device to USB device driver + */ + Error *e = nullptr; + DeviceClass *usb_device_class = &Object_pool::p()->obj[Object_pool::USB_DEVICE]._device_class; + usb_device_class->realize(dev_state, &e); + if (e) { + error_free(e); + e = nullptr; + + usb_device_class->unrealize(dev_state, &e); + Object_pool::p()->free_object(obj); + + return nullptr; + } + + return &obj->_usb_host_device; +} + + +void remove_usbdevice(USBHostDevice *device) +{ + DeviceClass *usb_device_class = &Object_pool::p()->obj[Object_pool::USB_DEVICE]._device_class; + DeviceState *usb_device_state = cast_DeviceState(device); + + if (usb_device_class == nullptr) + PERR("usb_device_class null"); + + if (usb_device_state == nullptr) + PERR("usb_device_class null"); + + Error *e = nullptr; + usb_device_class->unrealize(usb_device_state, &e); + + Wrapper *obj = Object_pool::p()->find_object(device); + Object_pool::p()->free_object(obj); +} + + +void reset_controller() +{ + Wrapper *w = &Object_pool::p()->obj[Object_pool::XHCI]; + DeviceClass *dc = &w->_device_class; + + dc->reset(&w->_device_state); +} + + +/*********************************** + ** Qemu interface implementation ** + ***********************************/ + +Type type_register_static(TypeInfo const *t) +{ + if (!Genode::strcmp(t->name, TYPE_XHCI)) { + Wrapper *w = &Object_pool::p()->obj[Object_pool::XHCI]; + w->_xhci_state = (XHCIState*) malloc(sizeof(XHCIState)); + Genode::memset(w->_xhci_state, 0, sizeof(XHCIState)); + + t->class_init(&w->_object_class, 0); + + properties_apply(w->_xhci_state, &w->_device_class); + + PCIDevice *pci = &w->_pci_device; + PCIDeviceClass *pci_class = &w->_pci_device_class; + Error *e; + pci_class->realize(pci, &e); + } + + if (!Genode::strcmp(t->name, TYPE_USB_DEVICE)) { + Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_DEVICE]; + t->class_init(&w->_object_class, 0); + } + + if (!Genode::strcmp(t->name, TYPE_USB_HOST_DEVICE)) { + Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_HOST_DEVICE]; + t->class_init(&w->_object_class, 0); + } + + if (!Genode::strcmp(t->name, TYPE_USB_BUS)) { + Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_BUS]; + ObjectClass *c = &w->_object_class; + t->class_init(c, 0); + } + + return nullptr; +} + + +void qbus_create_inplace(void* bus, size_t size , const char* type, + DeviceState *parent, const char *name) +{ + Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_BUS]; + BusState *b = &w->_bus_state; + char const *n = "xhci.0"; + b->name = (char *)malloc(Genode::strlen(n) + 1); + Genode::strncpy(b->name, n, Genode::strlen(n) + 1); +} + + +void timer_del(QEMUTimer *t) +{ + _timer_queue->deactivate_timer(t); +} + + +void timer_free(QEMUTimer *t) +{ + _timer_queue->delete_timer(t); + free(t); +} + + +void timer_mod(QEMUTimer *t, int64_t expire) +{ + _timer_queue->activate_timer(t, expire); +} + + +QEMUTimer* timer_new_ns(QEMUClockType, void (*cb)(void*), void *opaque) +{ + QEMUTimer *t = (QEMUTimer*)malloc(sizeof(QEMUTimer)); + if (t == nullptr) { + PERR("could not create QEMUTimer"); + return nullptr; + } + + _timer_queue->register_timer(t, cb, opaque); + + return t; +} + + +int64_t qemu_clock_get_ns(QEMUClockType) { + return _timer_queue->get_ns(); } + + +struct Controller : public Qemu::Controller +{ + struct Mmio + { + Genode::addr_t id; + Genode::size_t size; + Genode::off_t offset; + + MemoryRegionOps const *ops; + } mmio_regions [16]; + + uint64_t _mmio_size; + + Controller() + { + Genode::memset(mmio_regions, 0, sizeof(mmio_regions)); + } + + void mmio_add_region(uint64_t size) { _mmio_size = size; } + + void mmio_add_region_io(Genode::addr_t id, uint64_t size, MemoryRegionOps const *ops) + { + for (Mmio &mmio : mmio_regions) { + if (mmio.id != 0) continue; + + mmio.id = id; + mmio.size = size; + mmio.ops = ops; + return; + } + } + + Mmio &find_region(Genode::off_t offset) + { + for (Mmio &mmio : mmio_regions) { + if (offset >= mmio.offset + && (offset < mmio.offset + (Genode::off_t)mmio.size)) + return mmio; + } + + PERR("could not find MMIO region for offset: %lx", offset); + throw -1; + } + + void mmio_add_sub_region(Genode::addr_t id, Genode::off_t offset) + { + for (Mmio &mmio : mmio_regions) { + if (mmio.id != id) continue; + + mmio.offset = offset; + return; + } + } + + size_t mmio_size() const { return _mmio_size; } + + int mmio_read(Genode::off_t offset, void *buf, size_t size) + { + Genode::Lock::Guard g(_lock); + Mmio &mmio = find_region(offset); + Genode::off_t reg = offset - mmio.offset; + + void *ptr = Object_pool::p()->xhci_state(); + + /* + * Handle port access + */ + if (offset >= (OFF_OPER + 0x400) && offset < OFF_RUNTIME) { + uint32_t port = (offset - 0x440) / 0x10; + ptr = (void*)&Object_pool::p()->xhci_state()->ports[port]; + } + + uint64_t v = mmio.ops->read(ptr, reg, size); + *((uint32_t*)buf) = v; + + if (verbose_mmio) + PDBG("mmio: %lx offset: %lx reg: %lx v: %llx", mmio.id, offset, reg, v); + + return 0; + } + + int mmio_write(Genode::off_t offset, void const *buf, size_t size) + { + Genode::Lock::Guard g(_lock); + Mmio &mmio = find_region(offset); + Genode::off_t reg = offset - mmio.offset; + uint64_t v = *((uint64_t*)buf); + + void *ptr = Object_pool::p()->xhci_state(); + + /* + * Handle port access + */ + if (offset >= (OFF_OPER + 0x400) && offset < OFF_RUNTIME) { + uint32_t port = (offset - 0x440) / 0x10; + ptr = (void*)&Object_pool::p()->xhci_state()->ports[port]; + } + + mmio.ops->write(ptr, reg, v, size); + + if (verbose_mmio) + PDBG("mmio: %lx offset: %lx reg: %lx v: %llx", mmio.id, offset, reg, v); + + return 0; + } +}; + + +static Controller *controller() +{ + static Controller _inst; + return &_inst; +} + +Qemu::Controller *qemu_controller() +{ + return controller(); +} + + +/********** + ** MMIO ** + **********/ + +void memory_region_init(MemoryRegion *mr, Object *obj, const char *name, uint64_t size) { + controller()->mmio_add_region(size); } + + +void memory_region_init_io(MemoryRegion* mr, Object* obj, const MemoryRegionOps* ops, + void *, const char * name, uint64_t size) { + controller()->mmio_add_region_io((Genode::addr_t)mr, size, ops); } + + +void memory_region_add_subregion(MemoryRegion *mr, hwaddr offset, MemoryRegion *sr) { + controller()->mmio_add_sub_region((Genode::addr_t)sr, offset); } + + +/********* + ** DMA ** + *********/ + +int pci_dma_read(PCIDevice*, dma_addr_t addr, void *buf, dma_addr_t size) +{ + return _pci_device->read_dma(addr, buf, size); +} + + +int pci_dma_write(PCIDevice*, dma_addr_t addr, const void *buf, dma_addr_t size) +{ + return _pci_device->write_dma(addr, buf, size); +} + + +int dma_memory_read(AddressSpace*, dma_addr_t addr, void *buf, dma_addr_t size) +{ + return _pci_device->read_dma(addr, buf, size); +} + + +/**************** + ** Interrupts ** + ****************/ + +void pci_set_irq(PCIDevice*, int level) +{ + if (verbose_irq) + PDBG("IRQ level: %d", level); + _pci_device->raise_interrupt(level); +} + + +void pci_irq_assert(PCIDevice*) +{ + pci_set_irq(nullptr, 1); +} + + +int msi_init(PCIDevice *pdev, uint8_t offset, unsigned int nr_vectors, bool msi64bit, + bool msi_per_vector_mask) +{ + return 0; +} + + +int msix_init(PCIDevice*, short unsigned int, MemoryRegion*, uint8_t, unsigned int, MemoryRegion*, + uint8_t, unsigned int, uint8_t) +{ + return 0; +} + + +bool msi_enabled(const PCIDevice *pdev) +{ + return false; +} + + +int msix_enabled(PCIDevice *pdev) +{ + return 0; +} + + +void msi_notify(PCIDevice *pdev, unsigned int level) { } + + +void msix_notify(PCIDevice *pdev , unsigned int level) { } + + +/************************************* + ** IO vector / scatter-gatter list ** + *************************************/ + +void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len) +{ + int niov = qiov->niov; + + if (qiov->alloc_hint <= niov) { + if (verbose_iov) + PDBG("alloc_hint %d <= niov: %d", qiov->alloc_hint, niov); + + qiov->alloc_hint += 64; + iovec *new_iov = (iovec*) malloc(sizeof(iovec) * qiov->alloc_hint); + if (new_iov == nullptr) { + PERR("Could not reallocate iov"); + throw -1; + } + + for (int i = 0; i < niov; i++) { + new_iov[i].iov_base = qiov->iov[i].iov_base; + new_iov[i].iov_len = qiov->iov[i].iov_len; + } + + free(qiov->iov); + qiov->iov = new_iov; + } + + if (verbose_iov) + PDBG("niov: %u iov_base: %p base: %p len: %zu", + niov, &qiov->iov[niov].iov_base, base, len); + + qiov->iov[niov].iov_base = base; + qiov->iov[niov].iov_len = len; + qiov->size += len; + qiov->niov++; +} + + +void qemu_iovec_destroy(QEMUIOVector *qiov) +{ + qemu_iovec_reset(qiov); + + free(qiov->iov); + qiov->iov = nullptr; +} + + +void qemu_iovec_reset(QEMUIOVector *qiov) +{ + qiov->size = 0; + qiov->niov = 0; +} + + +void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint) +{ + if (verbose_iov) + PDBG("iov: %p alloc_hint: %d", qiov->iov, alloc_hint); + + iovec *iov = qiov->iov; + if (iov != nullptr) { + if (alloc_hint > qiov->alloc_hint) + PERR("iov already initialized: %p and alloc_hint smaller", iov); + + qemu_iovec_reset(qiov); + return; + } + + if (alloc_hint <= 0) alloc_hint = 1; + + qiov->alloc_hint = alloc_hint; + + qiov->iov = (iovec*) malloc(sizeof(iovec) * alloc_hint); + if (qiov->iov == nullptr) { + PERR("Could not allocate iov"); + throw -1; + } + + Genode::memset(qiov->iov, 0, sizeof(iovec) * alloc_hint); + + qemu_iovec_reset(qiov); +} + + +/* taken from util/iov.c */ +size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, + size_t offset, const void *buf, size_t bytes) +{ + size_t done; + unsigned int i; + for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) { + if (offset < iov[i].iov_len) { + size_t len = Genode::min(iov[i].iov_len - offset, bytes - done); + memcpy((char*)iov[i].iov_base + offset, (char*)buf + done, len); + done += len; + offset = 0; + } else { + offset -= iov[i].iov_len; + } + } + + assert(offset == 0); + return done; +} + + +/* taken from util/iov.c */ +size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, + size_t offset, void *buf, size_t bytes) +{ + size_t done; + unsigned int i; + for (i = 0, done = 0; (offset || done < bytes) && i < iov_cnt; i++) { + if (offset < iov[i].iov_len) { + size_t len = Genode::min(iov[i].iov_len - offset, bytes - done); + memcpy((char*)buf + done, (char*)iov[i].iov_base + offset, len); + done += len; + offset = 0; + } else { + offset -= iov[i].iov_len; + } + } + + assert(offset == 0); + return done; +} + + +void pci_dma_sglist_init(QEMUSGList *sgl, PCIDevice*, int alloc_hint) { + qemu_iovec_init(sgl, alloc_hint); } + + +void qemu_sglist_add(QEMUSGList *sgl, dma_addr_t base, dma_addr_t len) { + qemu_iovec_add(sgl, (void*)base, len); } + + +void qemu_sglist_destroy(QEMUSGList *sgl) { + qemu_iovec_destroy(sgl); } + + +int usb_packet_map(USBPacket *p, QEMUSGList *sgl) +{ + void *mem; + + for (int i = 0; i < sgl->niov; i++) { + dma_addr_t base = (dma_addr_t) sgl->iov[i].iov_base; + dma_addr_t len = sgl->iov[i].iov_len; + + while (len) { + dma_addr_t xlen = len; + mem = _pci_device->map_dma(base, xlen); + if (verbose_iov) + PDBG("mem: 0x%p base: 0x%p len: 0x%lx", mem, (void*)base, len); + + if (!mem) { + goto err; + } + if (xlen > len) { + xlen = len; + } + qemu_iovec_add(&p->iov, mem, xlen); + len -= xlen; + base += xlen; + } + } + return 0; + +err: + PERR("could not map dma"); + usb_packet_unmap(p, sgl); + return -1; +} + + +void usb_packet_unmap(USBPacket *p, QEMUSGList *sgl) +{ + for (int i = 0; i < p->iov.niov; i++) { + _pci_device->unmap_dma(p->iov.iov[i].iov_base, + p->iov.iov[i].iov_len); + } +} + + +/****************** + ** qapi/error.h ** + ******************/ + +void error_setg(Error **errp, const char *fmt, ...) +{ + assert(*errp == nullptr); + + *errp = (Error*) malloc(sizeof(Error)); + if (*errp == nullptr) { + PERR("Could not allocate Error"); + return; + } + + Error *e = *errp; + + va_list args; + + va_start(args, fmt); + Genode::String_console sc(e->string, sizeof(e->string)); + sc.vprintf(fmt, args); + va_end(args); +} + + +void error_propagate(Error **dst_errp, Error *local_err) { + *dst_errp = local_err; } + + +void error_free(Error *err) { free(err); } diff --git a/repos/libports/src/lib/qemu-usb/symbol.map b/repos/libports/src/lib/qemu-usb/symbol.map new file mode 100644 index 0000000000..f3aea3ca8a --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/symbol.map @@ -0,0 +1,14 @@ +/* + * + */ +{ + global: + + extern "C++" { + Qemu::*; + }; + + local: + + *; +};