diff --git a/arch/x86/include/arch/arch_ops.h b/arch/x86/include/arch/arch_ops.h index b3092d602..0f10feb35 100644 --- a/arch/x86/include/arch/arch_ops.h +++ b/arch/x86/include/arch/arch_ops.h @@ -91,5 +91,4 @@ static inline uint arch_curr_cpu_num(void) { #define smp_rmb() CF #endif - #endif // !ASSEMBLY diff --git a/dev/bus/pci/bus_mgr/bus_mgr.cpp b/dev/bus/pci/bus_mgr/bus_mgr.cpp index 9c9756afe..75e46d823 100644 --- a/dev/bus/pci/bus_mgr/bus_mgr.cpp +++ b/dev/bus/pci/bus_mgr/bus_mgr.cpp @@ -323,6 +323,18 @@ status_t pci_bus_mgr_allocate_irq(const pci_location_t loc, uint *irqbase) { return d->allocate_irq(irqbase); } +ssize_t pci_read_vendor_capability(const pci_location_t loc, size_t index, void *buf, size_t buflen) { + char str[14]; + LTRACEF("%s\n", pci_loc_string(loc, str)); + + device *d = lookup_device_by_loc(loc); + if (!d) { + return ERR_NOT_FOUND; + } + + return d->read_vendor_capability(index, buf, buflen); +} + void pci_dump_bar(const pci_bar_t *bar, int index) { if (bar->addr >= UINT32_MAX || bar->size >= UINT32_MAX) { printf("BAR %d: addr %-#16llx size %-#16zx io %d 64b %d pref %d\n", diff --git a/dev/bus/pci/bus_mgr/device.cpp b/dev/bus/pci/bus_mgr/device.cpp index ac071d634..ec5242b9c 100644 --- a/dev/bus/pci/bus_mgr/device.cpp +++ b/dev/bus/pci/bus_mgr/device.cpp @@ -133,6 +133,14 @@ void device::dump(size_t indent) { pci_dump_bar(bars_ + b, b); } } + + capability *cap; + list_for_every_entry(&capability_list_, cap, capability, node) { + for (size_t i = 0; i < indent + 2; i++) { + printf(" "); + } + printf("capability: offset %#x id %#x\n", cap->config_offset, cap->id); + } } status_t device::enable() { @@ -220,6 +228,27 @@ status_t device::probe_capabilities() { return NO_ERROR; } +ssize_t device::read_vendor_capability(size_t index, void *buf, size_t buflen) { + const capability *cap; + list_for_every_entry(&capability_list_, cap, capability, node) { + if (cap->id == 0x9) { // vendor specific + if (index == 0) { + uint8_t len; + pci_read_config_byte(loc(), cap->config_offset + 2, &len); + + const size_t readlen = MIN(len, buflen); + for (size_t i = 0; i < readlen; i++) { + pci_read_config_byte(loc(), cap->config_offset + i, static_cast(buf) + i); + } + return len; + } + index--; + } + } + + return ERR_NOT_FOUND; +} + status_t device::init_msi_capability(capability *cap) { LTRACE_ENTRY; @@ -256,16 +285,21 @@ status_t device::init_msix_capability(capability *cap) { status_t device::allocate_irq(uint *irq) { LTRACE_ENTRY; - uint8_t interrupt_line; - status_t err = pci_read_config_byte(loc(), PCI_CONFIG_INTERRUPT_LINE, &interrupt_line); + uint8_t interrupt_pin; + status_t err = pci_read_config_byte(loc(), PCI_CONFIG_INTERRUPT_PIN, &interrupt_pin); if (err != NO_ERROR) return err; - if (interrupt_line == 0) { + if (interrupt_pin == 0) { return ERR_NO_RESOURCES; } // map the irq number in config space to platform vector space - err = platform_pci_int_to_vector(interrupt_line, irq); + err = platform_pci_int_to_vector(interrupt_pin, irq); + if (err != NO_ERROR) return err; + + // write it back to the pci config in the interrupt line offset + pci_write_config_byte(loc(), PCI_CONFIG_INTERRUPT_LINE, *irq); + return err; } diff --git a/dev/bus/pci/bus_mgr/device.h b/dev/bus/pci/bus_mgr/device.h index ae2b9f28c..df2a6d576 100644 --- a/dev/bus/pci/bus_mgr/device.h +++ b/dev/bus/pci/bus_mgr/device.h @@ -90,6 +90,7 @@ class device { uint8_t header_type() const { return config_.header_type & PCI_HEADER_TYPE_MASK; } status_t read_bars(pci_bar_t bar[6]); + ssize_t read_vendor_capability(size_t index, void *buf, size_t buflen); bool has_msi() const { return msi_cap_; } bool has_msix() const { return msix_cap_; } diff --git a/dev/bus/pci/debug.cpp b/dev/bus/pci/debug.cpp index 066128933..ad227ae6a 100644 --- a/dev/bus/pci/debug.cpp +++ b/dev/bus/pci/debug.cpp @@ -54,10 +54,11 @@ static void pci_list(void) { if (config.vendor_id != 0xffff) { printf("%04x:%02x:%02x.%0x vendor_id=%04x device_id=%04x, header_type=%02x " - "base_class=%02x, sub_class=%02x, interface=%02x, irq=%u\n", + "base_class=%02x, sub_class=%02x, interface=%02x, irq_line=%u, irq_pin=%u\n", state.segment, state.bus, state.dev, state.fn, config.vendor_id, config.device_id, config.header_type, config.base_class, - config.sub_class, config.program_interface, config.type0.interrupt_line); + config.sub_class, config.program_interface, config.type0.interrupt_line, + config.type0.interrupt_pin); devices++; lines++; } diff --git a/dev/bus/pci/drivers/rules.mk b/dev/bus/pci/drivers/rules.mk index 6a3ac16c5..ea86441d3 100644 --- a/dev/bus/pci/drivers/rules.mk +++ b/dev/bus/pci/drivers/rules.mk @@ -1,5 +1,7 @@ # Fake module that just declares deps on all the PCI drivers in the system. # MODULES += dev/bus/pci - MODULES += dev/net/e1000 +MODULES += dev/virtio/block +MODULES += dev/virtio/net +MODULES += dev/virtio/gpu diff --git a/dev/bus/pci/include/dev/bus/pci.h b/dev/bus/pci/include/dev/bus/pci.h index 0368205e3..b99a05cea 100644 --- a/dev/bus/pci/include/dev/bus/pci.h +++ b/dev/bus/pci/include/dev/bus/pci.h @@ -98,6 +98,9 @@ status_t pci_bus_mgr_allocate_msi(const pci_location_t loc, size_t num_requested // allocate a regular irq for this device and return it in irqbase status_t pci_bus_mgr_allocate_irq(const pci_location_t loc, uint *irqbase); +// XXX sort this nicely +ssize_t pci_read_vendor_capability(const pci_location_t loc, size_t index, void *buf, size_t buflen); + // return a pointer to a formatted string const char *pci_loc_string(pci_location_t loc, char out_str[14]); diff --git a/dev/virtio/include/dev/virtio/virtio-bus.h b/dev/virtio/include/dev/virtio/virtio-bus.h index 96c364fbb..2f0ba7248 100644 --- a/dev/virtio/include/dev/virtio/virtio-bus.h +++ b/dev/virtio/include/dev/virtio/virtio-bus.h @@ -21,4 +21,8 @@ class virtio_bus { virtual void virtio_status_driver_ok() = 0; virtual void virtio_kick(uint16_t ring_index) = 0; virtual void register_ring(uint32_t page_size, uint32_t queue_sel, uint32_t queue_num, uint32_t queue_align, uint32_t queue_pfn) = 0; + + uint64_t virtio_read_host_feature_word_64(uint32_t word) { + return virtio_read_host_feature_word(word) | static_cast(virtio_read_host_feature_word(word + 1)) << 32; + } }; \ No newline at end of file diff --git a/dev/virtio/include/dev/virtio/virtio-device.h b/dev/virtio/include/dev/virtio/virtio-device.h index c7baff33f..cdecb40b7 100644 --- a/dev/virtio/include/dev/virtio/virtio-device.h +++ b/dev/virtio/include/dev/virtio/virtio-device.h @@ -13,6 +13,7 @@ #include #include #include +#include class virtio_device { public: @@ -52,6 +53,7 @@ class virtio_device { void *get_config_ptr() { return config_ptr_; } const void *get_config_ptr() const { return config_ptr_; } + void set_config_ptr(void *ptr) { config_ptr_ = ptr; } using irq_driver_callback = enum handler_return (*)(virtio_device *dev, uint ring, const vring_used_elem *e); using config_change_callback = enum handler_return (*)(virtio_device *dev); @@ -61,6 +63,17 @@ class virtio_device { config_change_callback_ = config; } + // TODO: move this into the bus layer + void set_irq(uint irq) { irq_ = irq; } + + void mask_interrupt() { + ::mask_interrupt(irq_); + } + + void unmask_interrupt() { + ::unmask_interrupt(irq_); + } + // From low level bus layer handler_return handle_queue_interrupt(); handler_return handle_config_interrupt(); diff --git a/dev/virtio/include/dev/virtio/virtio-pci-bus.h b/dev/virtio/include/dev/virtio/virtio-pci-bus.h new file mode 100644 index 000000000..e4bcacd1f --- /dev/null +++ b/dev/virtio/include/dev/virtio/virtio-pci-bus.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 Travis Geiselbrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#pragma once + +#include +#include + +#include + +#include + +struct virtio_pci_common_cfg; + +class virtio_pci_bus final : public virtio_bus { +public: + virtio_pci_bus() = default; + ~virtio_pci_bus() override = default; + + void virtio_reset_device() override; + void virtio_status_acknowledge_driver() override; + uint32_t virtio_read_host_feature_word(uint32_t word) override; + void virtio_set_guest_features(uint32_t word, uint32_t features) override; + void virtio_status_driver_ok() override; + void virtio_kick(uint16_t ring_index) override; + + void register_ring(uint32_t page_size, uint32_t queue_sel, uint32_t queue_num, uint32_t queue_align, uint32_t queue_pfn) override; + + status_t init(pci_location_t loc, size_t index); + + static handler_return virtio_pci_irq(void *arg); + + volatile virtio_pci_common_cfg *common_config() { + return reinterpret_cast(config_ptr(common_cfg_)); + } + + void *device_config() { + return reinterpret_cast(config_ptr(device_cfg_)); + } + +//private: + struct config_pointer { + bool valid; + int bar; + size_t offset; + size_t length; + }; + + struct mapped_bars { + bool mapped; + void *vaddr; + }; + + pci_location_t loc_ = {}; + + mapped_bars bar_map_[6] = {}; + + config_pointer common_cfg_ = {}; + config_pointer notify_cfg_ = {}; + config_pointer isr_cfg_ = {}; + config_pointer device_cfg_ = {}; + config_pointer pci_cfg_ = {}; + + uint32_t notify_offset_multiplier_ = {}; + + // TODO: handle MSI-X + uint32_t irq_ = {}; + + void *config_ptr(const config_pointer &cfg) { + if (!cfg.valid) { + return nullptr; + } + + auto &bar = bar_map_[cfg.bar]; + return (void *)((uintptr_t)bar.vaddr + cfg.offset); + } + +}; \ No newline at end of file diff --git a/dev/virtio/net/virtio-net.cpp b/dev/virtio/net/virtio-net.cpp index 40b2400a9..e12df096b 100644 --- a/dev/virtio/net/virtio-net.cpp +++ b/dev/virtio/net/virtio-net.cpp @@ -195,7 +195,7 @@ status_t virtio_net_init(virtio_device *dev) { dev->bus()->virtio_status_acknowledge_driver(); // XXX check features bits and ack/nak them - uint64_t host_features = dev->bus()->virtio_read_host_feature_word(0) | (uint64_t)dev->bus()->virtio_read_host_feature_word(1) << 32; + uint64_t host_features = dev->bus()->virtio_read_host_feature_word_64(0); dump_feature_bits(host_features); /* set our irq handler */ diff --git a/dev/virtio/rules.mk b/dev/virtio/rules.mk index 5c192788a..4df8f2d35 100644 --- a/dev/virtio/rules.mk +++ b/dev/virtio/rules.mk @@ -2,9 +2,10 @@ LOCAL_DIR := $(GET_LOCAL_DIR) MODULE := $(LOCAL_DIR) -MODULE_SRCS += $(LOCAL_DIR)/virtio.cpp MODULE_SRCS += $(LOCAL_DIR)/virtio-bus.cpp MODULE_SRCS += $(LOCAL_DIR)/virtio-device.cpp MODULE_SRCS += $(LOCAL_DIR)/virtio-mmio-bus.cpp +MODULE_SRCS += $(LOCAL_DIR)/virtio-pci-bus.cpp +MODULE_SRCS += $(LOCAL_DIR)/virtio.cpp include make/module.mk diff --git a/dev/virtio/virtio-mmio-bus.cpp b/dev/virtio/virtio-mmio-bus.cpp index 221d56064..ffec424be 100644 --- a/dev/virtio/virtio-mmio-bus.cpp +++ b/dev/virtio/virtio-mmio-bus.cpp @@ -79,13 +79,6 @@ STATIC_ASSERT(sizeof(struct virtio_mmio_config) == 0x100); #define VIRTIO_MMIO_MAGIC 0x74726976 // 'virt' -#define VIRTIO_STATUS_ACKNOWLEDGE (1<<0) -#define VIRTIO_STATUS_DRIVER (1<<1) -#define VIRTIO_STATUS_DRIVER_OK (1<<2) -#define VIRTIO_STATUS_FEATURES_OK (1<<3) -#define VIRTIO_STATUS_DEVICE_NEEDS_RESET (1<<6) -#define VIRTIO_STATUS_FAILED (1<<7) - // TODO: switch to using reg.h mmio_ accessors void virtio_mmio_bus::virtio_reset_device() { mmio_config_->status = 0; @@ -199,7 +192,7 @@ int virtio_mmio_detect(void *ptr, uint count, const uint irqs[], size_t stride) dev->index_ = i; dev->irq_ = irqs[i]; - dev->config_ptr_ = (void *)mmio->config; + dev->set_config_ptr((void *)mmio->config); mask_interrupt(irqs[i]); register_int_handler(irqs[i], &virtio_mmio_bus::virtio_mmio_irq, static_cast(dev)); diff --git a/dev/virtio/virtio-pci-bus.cpp b/dev/virtio/virtio-pci-bus.cpp new file mode 100644 index 000000000..19abe5f34 --- /dev/null +++ b/dev/virtio/virtio-pci-bus.cpp @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2024 Travis Geiselbrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#include "lk/debug.h" +#if WITH_DEV_BUS_PCI + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if WITH_KERNEL_VM +#include +#endif +#if WITH_DEV_VIRTIO_BLOCK +#include +#endif + +#define LOCAL_TRACE 1 + +#include + +#include "virtio_priv.h" + +struct virtio_pci_devices { + uint16_t device_id; + bool legacy; + status_t (*init)(pci_location_t loc, const virtio_pci_devices &dev_table_entry, size_t ordinal); +}; + +static status_t init_block(pci_location_t loc, const virtio_pci_devices &dev_table_entry, size_t ordinal); + +const virtio_pci_devices devices[] = { + { 0x1000, true, nullptr }, // transitional network + { 0x1001, true, &init_block }, // transitional block + { 0x1009, true, nullptr }, // legacy virtio 9p + { 0x1041, false, nullptr }, // non-transitional network + { 0x1042, false, &init_block }, // non-transitional block + { 0x1043, false, nullptr }, // non-transitional console + { 0x1050, false, nullptr }, // non-transitional gpu + { 0x1052, false, nullptr }, // non-transitional input +}; + +struct virtio_pci_cap { + uint8_t cap_vndr; + uint8_t cap_next; + uint8_t cap_len; + /* Common configuration */ +#define VIRTIO_PCI_CAP_COMMON_CFG 1 + /* Notifications */ +#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 + /* ISR Status */ +#define VIRTIO_PCI_CAP_ISR_CFG 3 + /* Device specific configuration */ +#define VIRTIO_PCI_CAP_DEVICE_CFG 4 + /* PCI configuration access */ +#define VIRTIO_PCI_CAP_PCI_CFG 5 + /* Shared memory region */ +#define VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8 + /* Vendor-specific data */ +#define VIRTIO_PCI_CAP_VENDOR_CFG 9 + uint8_t cfg_type; + uint8_t bar; + uint8_t id; + uint8_t padding[2]; + uint32_t offset; + uint32_t length; +}; + +STATIC_ASSERT(sizeof(virtio_pci_cap) == 16); + +static void dump_pci_cap(const virtio_pci_cap *cap) { + const char *type; + + switch (cap->cfg_type) { + case VIRTIO_PCI_CAP_COMMON_CFG: type = "common"; break; + case VIRTIO_PCI_CAP_NOTIFY_CFG: type = "notify"; break; + case VIRTIO_PCI_CAP_ISR_CFG: type = "isr"; break; + case VIRTIO_PCI_CAP_DEVICE_CFG: type = "device"; break; + case VIRTIO_PCI_CAP_PCI_CFG: type = "pci"; break; + case VIRTIO_PCI_CAP_SHARED_MEMORY_CFG: type = "shared mem"; break; + case VIRTIO_PCI_CAP_VENDOR_CFG: type = "vendor"; break; + default: type = "unknown"; break; + } + + printf("PCI capability: vendor %#hhx next %#hhx len %#hhx type %#hhx (%s) bar %#hhx id %#hhx offset %#x length %#x\n", + cap->cap_vndr, cap->cap_next, cap->cap_len, cap->cfg_type, type, cap->bar, cap->id, cap->offset, cap->length); +} + +struct virtio_pci_notify_cap { + virtio_pci_cap cap; + uint32_t notify_off_multiplier; +}; + +STATIC_ASSERT(sizeof(virtio_pci_notify_cap) == 20); + +struct virtio_pci_common_cfg { + /* About the whole device. */ + uint32_t device_feature_select; /* read-write */ + uint32_t device_feature; /* read-only for driver */ + uint32_t driver_feature_select; /* read-write */ + uint32_t driver_feature; /* read-write */ + uint16_t config_msix_vector; /* read-write */ + uint16_t num_queues; /* read-only for driver */ + uint8_t device_status; /* read-write */ + uint8_t config_generation; /* read-only for driver */ + + /* About a specific virtqueue. */ + uint16_t queue_select; /* read-write */ + uint16_t queue_size; /* read-write */ + uint16_t queue_msix_vector; /* read-write */ + uint16_t queue_enable; /* read-write */ + uint16_t queue_notify_off; /* read-only for driver */ + uint64_t queue_desc; /* read-write */ + uint64_t queue_driver; /* read-write */ + uint64_t queue_device; /* read-write */ + uint16_t queue_notif_config_data; /* read-only for driver */ + uint16_t queue_reset; /* read-write */ + + /* About the administration virtqueue. */ + uint16_t admin_queue_index; /* read-only for driver */ + uint16_t admin_queue_num; /* read-only for driver */ + + void dump() volatile const { + printf("PCI common config @%p:\n", this); + printf("\tdevice feature select %#x features %#x\n", device_feature_select, device_feature); + printf("\tdriver feature select %#x features %#x\n", driver_feature_select, driver_feature); + printf("\tmsix vector %#x num queues %#x status %#x config gen %#x\n", config_msix_vector, num_queues, + device_status, config_generation); + } +}; + +STATIC_ASSERT(sizeof(virtio_pci_common_cfg) == 64); + +void virtio_pci_bus::virtio_reset_device() { + common_config()->device_status = 0; + while (common_config()->device_status != 0) + ; +} + +void virtio_pci_bus::virtio_status_acknowledge_driver() { + common_config()->device_status |= VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER; +} + +void virtio_pci_bus::virtio_status_driver_ok() { + common_config()->device_status |= VIRTIO_STATUS_DRIVER_OK; +} + +uint32_t virtio_pci_bus::virtio_read_host_feature_word(uint32_t word) { + common_config()->device_feature_select = word; + return common_config()->device_feature; +} + +void virtio_pci_bus::virtio_set_guest_features(uint32_t word, uint32_t features) { + common_config()->driver_feature_select = word; + common_config()->driver_feature = features; +} + +void virtio_pci_bus::virtio_kick(uint16_t ring_index) { + auto *notify = (volatile uint16_t *)((uintptr_t)config_ptr(notify_cfg_) + ring_index * notify_offset_multiplier_); + + LTRACEF("notify ring %u ptr %p\n", ring_index, notify); + + *notify = ring_index; + mb(); +} + +void virtio_pci_bus::register_ring(uint32_t page_size, uint32_t queue_sel, uint32_t queue_num, uint32_t queue_align, uint32_t queue_pfn) { + auto *ccfg = common_config(); + + LTRACEF("queue %u size %u pfn %#x (address %#" PRIx64 ")\n", queue_sel, queue_num, queue_pfn, (uint64_t)queue_pfn * page_size); + + ccfg->queue_select = queue_sel; + LTRACEF("existing queue_size %u\n", ccfg->queue_size); + LTRACEF("notify off %u\n", ccfg->queue_notify_off); + + ccfg->queue_size = queue_num; + ccfg->queue_desc = static_cast(queue_pfn) * page_size; + ccfg->queue_enable = 1; +} + +handler_return virtio_pci_bus::virtio_pci_irq(void *arg) { + PANIC_UNIMPLEMENTED; +} + +status_t virtio_pci_bus::init(pci_location_t loc, size_t index) { + LTRACE_ENTRY; + + loc_ = loc; + + // read all of the capabilities for this virtio device + bool map_bars[6] = {}; + for (size_t i = 0;; i++) { + virtio_pci_cap cap; + ssize_t err = pci_read_vendor_capability(loc, i, &cap, sizeof(cap)); + if (err < NO_ERROR || (size_t)err < sizeof(cap)) { + break; + } + if (LOCAL_TRACE) dump_pci_cap(&cap); + + // save off the bar + range of all of the capabilities we care about + virtio_pci_bus::config_pointer *cfg; + switch (cap.cfg_type) { + case VIRTIO_PCI_CAP_COMMON_CFG: + cfg = &common_cfg_; + goto common; + case VIRTIO_PCI_CAP_NOTIFY_CFG: { + // read in the extra 32bit offset multiplier + virtio_pci_notify_cap cap2; + err = pci_read_vendor_capability(loc, i, &cap2, sizeof(cap2)); + if (err < NO_ERROR || (size_t)err < sizeof(cap2)) { + break; + } + LTRACEF("notify offset multiplier %u\n", cap2.notify_off_multiplier); + notify_offset_multiplier_ = cap2.notify_off_multiplier; + + cfg = ¬ify_cfg_; + } + goto common; + case VIRTIO_PCI_CAP_ISR_CFG: + cfg = &isr_cfg_; + goto common; + case VIRTIO_PCI_CAP_DEVICE_CFG: + cfg = &device_cfg_; + goto common; + case VIRTIO_PCI_CAP_PCI_CFG: + cfg = &pci_cfg_; + // fallthrough +common: + DEBUG_ASSERT(cfg); + cfg->valid = true; + cfg->bar = cap.bar; + cfg->offset = cap.offset; + cfg->length = cap.length; + if (cap.bar < 6) { + map_bars[cap.bar] = true; + } + } + } + + // check that at least the mandatory capabilities are present + if (!(common_cfg_.valid && notify_cfg_.valid && isr_cfg_.valid && pci_cfg_.valid)) { + return ERR_NOT_FOUND; + } + + // map in the bars we care about + pci_bar_t bars[6]; + status_t err = pci_bus_mgr_read_bars(loc, bars); + if (err != NO_ERROR) { + return err; + } + + LTRACEF("virtio-pci BARS:\n"); + if (LOCAL_TRACE) pci_dump_bars(bars, 6); + + for (int i = 0; i < 6; i++) { + if (map_bars[i] && !bars[i].io) { + auto &bar_map = bar_map_[i]; +#if WITH_KERNEL_VM + char str[32]; + snprintf(str, sizeof(str), "virtio%zu bar%d", index, i); + err = vmm_alloc_physical(vmm_get_kernel_aspace(), str, bars[i].size, &bar_map.vaddr, 0, + bars[i].addr, /* vmm_flags */ 0, ARCH_MMU_FLAG_UNCACHED_DEVICE); + if (err != NO_ERROR) { + printf("error mapping bar %d\n", i); + continue; + } + bar_map.mapped = true; + printf("bar %d mapped at %p\n", i, bar_map.vaddr); +#else + // no need to map, it's already available at the physical address + if (sizeof(void *) < 8 && (bars[i].addr + bars[i].size) > UINT32_MAX) { + TRACEF("aborting due to 64bit BAR on 32bit arch\n"); + return ERR_NO_MEMORY; + } + bar_map.vaddr = (void *)(uintptr_t)bars[i].addr; +#endif + } + } + + // enable the device + pci_bus_mgr_enable_device(loc); + + // look at the common configuration + if (LOCAL_TRACE) common_config()->dump(); + + // read the device-independent feature bits + uint64_t features = virtio_read_host_feature_word_64(0); + virtio_dump_device_independent_features_bits(features); + + // accept mandatory features + if (features & VIRTIO_F_VERSION_1) { + virtio_set_guest_features(1, VIRTIO_F_VERSION_1 >> 32); + } + + uint irq_base; + err = pci_bus_mgr_allocate_msi(loc_, 1, &irq_base); + if (err != NO_ERROR) { + // fall back to regular IRQs + err = pci_bus_mgr_allocate_irq(loc_, &irq_base); + if (err != NO_ERROR) { + printf("block: unable to allocate IRQ\n"); + return err; + } + mask_interrupt(irq_base); + register_int_handler(irq_base, virtio_pci_irq, this); + } else { + mask_interrupt(irq_base); + register_int_handler_msi(irq_base, virtio_pci_irq, this, true); + } + irq_ = irq_base; + LTRACEF("IRQ number %#x\n", irq_base); + + return NO_ERROR; +} + +static status_t init_block(pci_location_t loc, const virtio_pci_devices &dev_table_entry, size_t index) { + LTRACE_ENTRY; + +#if WITH_DEV_VIRTIO_BLOCK + // create a virtio_pci_bus object and initialize it based on the location + auto *pbus = new virtio_pci_bus(); + auto err = pbus->init(loc, index); + if (err != NO_ERROR) { + delete pbus; + return err; + } + + auto *dev = new virtio_device(pbus); + + dev->set_irq(pbus->irq_); + + // TODO: move the config pointer getter that devices use into the bus + dev->set_config_ptr(pbus->device_config()); + + err = virtio_block_init(dev, pbus->virtio_read_host_feature_word(0)); + if (err != NO_ERROR) { + PANIC_UNIMPLEMENTED; + } + + dev->unmask_interrupt(); + + return err; +#else + return ERR_NOT_FOUND; +#endif +} + +static void virtio_pci_init(uint level) { + LTRACE_ENTRY; + + for (auto &dev: devices) { + for (size_t i = 0; ; i++) { + pci_location_t loc; + status_t err = pci_bus_mgr_find_device(&loc, dev.device_id, 0x1af4, i); + if (err != NO_ERROR) { + break; + } + + char str[14]; + LTRACEF("virtio-pci: looking at device at %s\n", pci_loc_string(loc, str)); + + // call the init routine + if (dev.init) { + dev.init(loc, dev, i); + } + } + } + + LTRACE_EXIT; +} + +LK_INIT_HOOK(virtio_pci, &virtio_pci_init, LK_INIT_LEVEL_PLATFORM + 1); + +#endif + diff --git a/dev/virtio/virtio.cpp b/dev/virtio/virtio.cpp index 843b7f7a4..bf7cb8e5f 100644 --- a/dev/virtio/virtio.cpp +++ b/dev/virtio/virtio.cpp @@ -10,6 +10,10 @@ #include #include +#include +#include + +#include "virtio_priv.h" void virtio_dump_desc(const vring_desc &desc) { printf("vring descriptor %p\n", &desc); @@ -19,6 +23,23 @@ void virtio_dump_desc(const vring_desc &desc) { printf("\tnext 0x%hx\n", desc.next); } +void virtio_dump_device_independent_features_bits(uint64_t feature) { + printf("virtio device independent features (%#" PRIx64 "):", feature); + if (feature & VIRTIO_F_INDIRECT_DESC) printf(" INDIRECT_DESC"); + if (feature & VIRTIO_F_EVENT_IDX) printf(" EVENT_IDX"); + if (feature & VIRTIO_F_VERSION_1) printf(" VERSION_1"); + if (feature & VIRTIO_F_ACCESS_PLATFORM) printf(" ACCESS_PLATFORM"); + if (feature & VIRTIO_F_RING_PACKED) printf(" RING_PACKED"); + if (feature & VIRTIO_F_IN_ORDER) printf(" IN_ORDER"); + if (feature & VIRTIO_F_ORDER_PLATFORM) printf(" ORDER_PLATFORM"); + if (feature & VIRTIO_F_SR_IOV) printf(" SR_IOV"); + if (feature & VIRTIO_F_NOTIFICATION_DATA) printf(" NOTIFICATION_DATA"); + if (feature & VIRTIO_F_NOTIF_CONFIG_DATA) printf(" NOTIF_CONFIG_DATA"); + if (feature & VIRTIO_F_RING_RESET) printf(" RING_RESET"); + if (feature & VIRTIO_F_ADMIN_VQ) printf(" ADMIN_VQ"); + printf("\n"); +} + static void virtio_init(uint level) { } diff --git a/dev/virtio/virtio_priv.h b/dev/virtio/virtio_priv.h index 7c238a215..d1231aeca 100644 --- a/dev/virtio/virtio_priv.h +++ b/dev/virtio/virtio_priv.h @@ -7,4 +7,29 @@ */ #pragma once -// TODO: remove if nothing needs to be here \ No newline at end of file +#include + +// status bits used by multiple transport busses +#define VIRTIO_STATUS_ACKNOWLEDGE (1<<0) +#define VIRTIO_STATUS_DRIVER (1<<1) +#define VIRTIO_STATUS_DRIVER_OK (1<<2) +#define VIRTIO_STATUS_FEATURES_OK (1<<3) +#define VIRTIO_STATUS_DEVICE_NEEDS_RESET (1<<6) +#define VIRTIO_STATUS_FAILED (1<<7) + +// device independent feature bits +#define VIRTIO_F_INDIRECT_DESC (1<<28) +#define VIRTIO_F_EVENT_IDX (1<<29) +#define VIRTIO_F_VERSION_1 (1ULL<<32) +#define VIRTIO_F_ACCESS_PLATFORM (1ULL<<33) +#define VIRTIO_F_RING_PACKED (1ULL<<34) +#define VIRTIO_F_IN_ORDER (1ULL<<35) +#define VIRTIO_F_ORDER_PLATFORM (1ULL<<36) +#define VIRTIO_F_SR_IOV (1ULL<<37) +#define VIRTIO_F_NOTIFICATION_DATA (1ULL<<38) +#define VIRTIO_F_NOTIF_CONFIG_DATA (1ULL<<39) +#define VIRTIO_F_RING_RESET (1ULL<<40) +#define VIRTIO_F_ADMIN_VQ (1ULL<<41) + +void virtio_dump_device_independent_features_bits(uint64_t feature); + diff --git a/scripts/do-qemuarm b/scripts/do-qemuarm index ede28d90e..537b048bf 100755 --- a/scripts/do-qemuarm +++ b/scripts/do-qemuarm @@ -4,6 +4,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" function HELP { echo "help:" + echo "-p : override LK project" echo "-6 : 64bit arm" echo "-3 : cortex-m3 based platform" echo "-v : boot kernel at EL2" @@ -19,6 +20,7 @@ function HELP { echo "-t : a virtio tap network device" echo "-g : a virtio display" echo "-f : a virtio 9p device with a host shared directory" + echo "-P : PCI based virtio devices" echo echo "-h for help" echo "all arguments after -- are passed to qemu directly" @@ -38,12 +40,13 @@ DO_CMPCTMALLOC=0 DO_MINIHEAP=0 DO_V9P=0 DO_V9P_DIR="" +DO_PCI_VIRTIO=0 SMP=1 MEMSIZE=512 SUDO="" PROJECT="" -while getopts cd:ghkm:Mnt36vp:s:f: FLAG; do +while getopts cd:ghkm:Mnt36vp:Ps:f: FLAG; do case $FLAG in c) DO_CMPCTMALLOC=1;; d) DO_DISK=1; DISK_IMAGE=$OPTARG;; @@ -59,6 +62,7 @@ while getopts cd:ghkm:Mnt36vp:s:f: FLAG; do m) MEMSIZE=$OPTARG;; s) SMP=$OPTARG;; p) PROJECT=$OPTARG;; + P) DO_PCI_VIRTIO=1;; h) HELP;; \?) echo unrecognized option @@ -100,14 +104,30 @@ fi ARGS=" -cpu $CPU -m $MEMSIZE -smp $SMP -machine $MACHINE -kernel build-${PROJECT}/lk.elf" +if (( $DO_PCI_VIRTIO )); then + VIRTIO_BLOCK_DEVICE="virtio-blk-pci" + VIRTIO_NET_DEVICE="virtio-net-pci" + VIRTIO_GPU_DEVICE="virtio-gpu-pci" + VIRTIO_KEYBOARD_DEVICE="virtio-keyboard-pci" + VIRTIO_MOUSE_DEVICE="virtio-mouse-pci" + VIRTIO_9P_DEVICE="virtio-9p-pci" +else + VIRTIO_BLOCK_DEVICE="virtio-blk-device" + VIRTIO_NET_DEVICE="virtio-net-device" + VIRTIO_GPU_DEVICE="virtio-gpu-device" + VIRTIO_KEYBOARD_DEVICE="virtio-keyboard-device" + VIRTIO_MOUSE_DEVICE="virtio-mouse-device" + VIRTIO_9P_DEVICE="virtio-9p-device" +fi + if (( $DO_DISK )); then ARGS+=" -drive if=none,file=${DISK_IMAGE},id=blk,format=raw" - ARGS+=" -device virtio-blk-device,drive=blk" + ARGS+=" -device ${VIRTIO_BLOCK_DEVICE},drive=blk" fi if (( $DO_NET )); then ARGS+=" -netdev user,id=vmnic,hostname=qemu " - ARGS+=" -device virtio-net-device,netdev=vmnic" + ARGS+=" -device ${VIRTIO_NET_DEVICE},netdev=vmnic" elif (( $DO_NET_TAP )); then # quick note to enable tap interface # IFNAME=qemu0 @@ -116,7 +136,7 @@ elif (( $DO_NET_TAP )); then # sudo ifconfig ${IFNAME} up # sudo ip link set ${IFNAME} master ${BRIDGE} ARGS+=" -netdev tap,id=vmnic,ifname=qemu0,script=no,downscript=no" - ARGS+=" -device virtio-net-device,netdev=vmnic" + ARGS+=" -device ${VIRTIO_NET_DEVICE},netdev=vmnic" #SUDO="sudo " else NO_NET_ARGS=" -net none" @@ -124,16 +144,16 @@ else fi if (( $DO_DISPLAY )); then - ARGS+=" -device virtio-gpu-device -serial stdio" - ARGS+=" -device virtio-keyboard-device" - ARGS+=" -device virtio-mouse-device" + ARGS+=" -device ${VIRTIO_GPU_DEVICE} -serial stdio" + ARGS+=" -device ${VIRTIO_KEYBOARD_DEVICE}" + ARGS+=" -device ${VIRTIO_MOUSE_DEVICE}" else ARGS+=" -nographic" fi if (( $DO_V9P )); then ARGS+=" -fsdev local,path=$DO_V9P_DIR,security_model=mapped,id=v9p0" - ARGS+=" -device virtio-9p-device,fsdev=v9p0,mount_tag=V9P0" + ARGS+=" -device ${VIRTIO_9P_DEVICE},fsdev=v9p0,mount_tag=V9P0" fi MAKE_VARS="" diff --git a/scripts/do-qemuriscv b/scripts/do-qemuriscv index ea341f9f0..6841ca79d 100755 --- a/scripts/do-qemuriscv +++ b/scripts/do-qemuriscv @@ -19,6 +19,7 @@ function HELP { echo "-n : a virtio network device" echo "-t : a virtio tap network device" echo "-g : a virtio display" + echo "-P : PCI based virtio devices" echo echo "-h for help" echo "all arguments after -- are passed to qemu directly" @@ -36,12 +37,13 @@ DO_DISPLAY=0 DO_CMPCTMALLOC=0 DO_MINIHEAP=0 DO_SUPERVISOR=0 +DO_PCI_VIRTIO=0 SMP=1 MEMSIZE=0 SUDO="" PROJECT="" -while getopts cd:ghm:Mmnteu6p:s:S FLAG; do +while getopts cd:ghm:Mmnteu6p:Ps:S FLAG; do case $FLAG in c) DO_CMPCTMALLOC=1;; d) DO_DISK=1; DISK_IMAGE=$OPTARG;; @@ -56,6 +58,7 @@ while getopts cd:ghm:Mmnteu6p:s:S FLAG; do s) SMP=$OPTARG;; S) DO_SUPERVISOR=1;; p) PROJECT=$OPTARG;; + P) DO_PCI_VIRTIO=1;; h) HELP;; \?) echo unrecognized option @@ -115,6 +118,22 @@ if [[ -z "$MEMSIZE" ]]; then PROJECT=$_MEMSIZE fi +if (( $DO_PCI_VIRTIO )); then + VIRTIO_BLOCK_DEVICE="virtio-blk-pci" + VIRTIO_NET_DEVICE="virtio-net-pci" + VIRTIO_GPU_DEVICE="virtio-gpu-pci" + VIRTIO_KEYBOARD_DEVICE="virtio-keyboard-pci" + VIRTIO_MOUSE_DEVICE="virtio-mouse-pci" + VIRTIO_9P_DEVICE="virtio-9p-pci" +else + VIRTIO_BLOCK_DEVICE="virtio-blk-device" + VIRTIO_NET_DEVICE="virtio-net-device" + VIRTIO_GPU_DEVICE="virtio-gpu-device" + VIRTIO_KEYBOARD_DEVICE="virtio-keyboard-device" + VIRTIO_MOUSE_DEVICE="virtio-mouse-device" + VIRTIO_9P_DEVICE="virtio-9p-device" +fi + # construct a list of args based on previous variables ARGS=" -machine $MACHINE" #ARGS+=",dumpdtb=riscv.dtb" # uncheck this to get a binary dump of the device tree for this config @@ -133,11 +152,11 @@ if [[ ! -z "$BIOS" ]]; then fi if (( $DO_DISK )); then ARGS+=" -drive if=none,file=${DISK_IMAGE},id=blk,format=raw" - ARGS+=" -device virtio-blk-device,drive=blk" + ARGS+=" -device ${VIRTIO_BLOCK_DEVICE},drive=blk" fi if (( $DO_NET )); then ARGS+=" -netdev user,id=vmnic,hostname=qemu" - ARGS+=" -device virtio-net-device,netdev=vmnic" + ARGS+=" -device ${VIRTIO_NET_DEVICE},netdev=vmnic" fi if (( $DO_NET_TAP )); then # quick note to enable tap interface @@ -147,13 +166,13 @@ if (( $DO_NET_TAP )); then # sudo ifconfig ${IFNAME} up # sudo ip link set ${IFNAME} master ${BRIDGE} ARGS+=" -netdev tap,id=vmnic,ifname=qemu0,script=no,downscript=no" - ARGS+=" -device virtio-net-device,netdev=vmnic" + ARGS+=" -device ${VIRTIO_NET_DEVICE},netdev=vmnic" #SUDO="sudo " fi if (( $DO_DISPLAY )); then - ARGS+=" -device virtio-gpu-device -serial stdio" - ARGS+=" -device virtio-keyboard-device" - ARGS+=" -device virtio-mouse-device" + ARGS+=" -device ${VIRTIO_GPU_DEVICE} -serial stdio" + ARGS+=" -device ${VIRTIO_KEYBOARD_DEVICE}" + ARGS+=" -device ${VIRTIO_MOUSE_DEVICE}" else ARGS+=" -nographic" fi