Index: sys/dev/hyperv/vmbus/vmbus.c =================================================================== --- sys/dev/hyperv/vmbus/vmbus.c +++ sys/dev/hyperv/vmbus/vmbus.c @@ -39,15 +39,21 @@ #include #include #include +#include #include #include #include #include +#include #include +#include #include #include +#include +#include +#include #include #include @@ -58,6 +64,7 @@ #include #include "acpi_if.h" +#include "pcib_if.h" #include "vmbus_if.h" #define VMBUS_GPADL_START 0xe1e10 @@ -74,6 +81,20 @@ uintptr_t *); static int vmbus_child_pnpinfo_str(device_t, device_t, char *, size_t); +static struct resource *vmbus_alloc_resource(device_t dev, + device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, + rman_res_t count, u_int flags); +static int vmbus_alloc_msi(device_t bus, device_t dev, + int count, int maxcount, int *irqs); +static int vmbus_release_msi(device_t bus, device_t dev, + int count, int *irqs); +static int vmbus_alloc_msix(device_t bus, device_t dev, + int *irq); +static int vmbus_release_msix(device_t bus, device_t dev, + int irq); +static int vmbus_map_msi(device_t bus, device_t dev, + int irq, uint64_t *addr, uint32_t *data); static uint32_t vmbus_get_version_method(device_t, device_t); static int vmbus_probe_guid_method(device_t, device_t, const struct hyperv_guid *); @@ -133,6 +154,22 @@ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_read_ivar, vmbus_read_ivar), DEVMETHOD(bus_child_pnpinfo_str, vmbus_child_pnpinfo_str), + DEVMETHOD(bus_alloc_resource, vmbus_alloc_resource), + DEVMETHOD(bus_release_resource, bus_generic_release_resource), + DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), + DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), + DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), +#if __FreeBSD_version >= 1100000 + DEVMETHOD(bus_get_cpus, bus_generic_get_cpus), +#endif + + /* pcib interface */ + DEVMETHOD(pcib_alloc_msi, vmbus_alloc_msi), + DEVMETHOD(pcib_release_msi, vmbus_release_msi), + DEVMETHOD(pcib_alloc_msix, vmbus_alloc_msix), + DEVMETHOD(pcib_release_msix, vmbus_release_msix), + DEVMETHOD(pcib_map_msi, vmbus_map_msi), /* Vmbus interface */ DEVMETHOD(vmbus_get_version, vmbus_get_version_method), @@ -974,6 +1011,85 @@ return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); } +/* + * We need the function to make sure the MMIO resource is allocated from the + * ranges found in _CRS. + * + * For the release function, we can use bus_generic_release_resource(). + */ +static struct resource * +vmbus_alloc_resource(device_t dev, device_t child, int type, int *rid, + rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) +{ + device_t parent = device_get_parent(dev); + int isdefault = RMAN_IS_DEFAULT_RANGE(start, end); + struct vmbus_mmio_entry *entry; + struct vmbus_softc *sc; + struct resource *res; + + if (type != SYS_RES_MEMORY) + return (BUS_ALLOC_RESOURCE(parent, child, type, rid, start, + end, count, flags)); + + sc = device_get_softc(dev); + TAILQ_FOREACH(entry, &sc->vmbus_mmio_list, link) { + if (isdefault || + (start <= entry->end && end >= entry->start)) { + if (isdefault) { + start = entry->start; + end = entry->end; + } + + res = BUS_ALLOC_RESOURCE(parent, child, type, rid, + MAX(start, entry->start), MIN(end, entry->end), + count, flags); + if (res != NULL) + return (res); + } + } + + return NULL; +} + +static device_t +get_nexus(device_t vmbus) +{ + device_t acpi = device_get_parent(vmbus); + device_t nexus = device_get_parent(acpi); + return (nexus); +} + +static int +vmbus_alloc_msi(device_t bus, device_t dev, int count, int maxcount, int *irqs) +{ + return (PCIB_ALLOC_MSI(get_nexus(bus), dev, count, maxcount, irqs)); +} + +static int +vmbus_release_msi(device_t bus, device_t dev, int count, int *irqs) +{ + return (PCIB_RELEASE_MSI(get_nexus(bus), dev, count, irqs)); +} + +static int +vmbus_alloc_msix(device_t bus, device_t dev, int *irq) +{ + return (PCIB_ALLOC_MSIX(get_nexus(bus), dev, irq)); +} + +static int +vmbus_release_msix(device_t bus, device_t dev, int irq) +{ + return (PCIB_RELEASE_MSIX(get_nexus(bus), dev, irq)); +} + +static int +vmbus_map_msi(device_t bus, device_t dev, int irq, uint64_t *addr, + uint32_t *data) +{ + return (PCIB_MAP_MSI(get_nexus(bus), dev, irq, addr, data)); +} + static uint32_t vmbus_get_version_method(device_t bus, device_t dev) { @@ -1001,6 +1117,126 @@ return (VMBUS_PCPU_GET(sc, vcpuid, cpu)); } +#define VTPM_BASE_ADDR 0xfed40000 +static ACPI_STATUS +parse_crs(ACPI_RESOURCE *res, void *ctx) +{ + device_t vmbus_dev = ctx; + struct vmbus_softc *sc = device_get_softc(vmbus_dev); + UINT64 start, end; + struct vmbus_mmio_entry *e; + + switch (res->Type) { + case ACPI_RESOURCE_TYPE_ADDRESS32: + start = res->Data.Address32.Address.Minimum; + end = res->Data.Address32.Address.Maximum; + break; + + case ACPI_RESOURCE_TYPE_ADDRESS64: + start = res->Data.Address64.Address.Minimum; + end = res->Data.Address64.Address.Maximum; + break; + + default: + /* Unused types. */ + return (AE_OK); + } + + /* + * We don't use <1MB addresses. + */ + if (end < 0x100000) + return (AE_OK); + + /* Don't conflict with vTPM. */ + if (end >= VTPM_BASE_ADDR && start < VTPM_BASE_ADDR) + end = VTPM_BASE_ADDR - 1; + + device_printf(vmbus_dev, "found _CRS [0x%lx, 0x%lx]\n", start, end); + + e = malloc(sizeof(struct vmbus_mmio_entry), M_DEVBUF, + M_WAITOK | M_ZERO); + e->start = start; + e->end = end; + + /* + * Put the >4GB range(s) at the beginning of the list. This way, when + * we iterate through the list to try to find a range for a 64-bit + * BAR, i.e., vmbus_alloc_resource(), we can make sure we try to use + * >4GB range first. + */ + if (e->start < (1ULL << 32)) + TAILQ_INSERT_TAIL(&sc->vmbus_mmio_list, e, link); + else + TAILQ_INSERT_HEAD(&sc->vmbus_mmio_list, e, link); + + return (AE_OK); +} + +static void +vmbus_get_crs(device_t dev, device_t vmbus_dev) +{ + ACPI_STATUS status; + + if (bootverbose) + device_printf(dev, "walking _CRS\n"); + + status = AcpiWalkResources(acpi_get_handle(dev), "_CRS", + parse_crs, vmbus_dev); + + if (bootverbose && ACPI_FAILURE(status)) + device_printf(dev, "_CRS: not found\n"); +} + +static void +vmbus_get_mmio_list(device_t dev) +{ + struct vmbus_softc *sc = device_get_softc(dev); + device_t acpi0, pcib0 = NULL; + device_t *children; + int i, count; + + TAILQ_INIT(&sc->vmbus_mmio_list); + + /* Try to find _CRS on VMBus device */ + vmbus_get_crs(dev, dev); + + /* Try to find _CRS on VMBus device's parent */ + acpi0 = device_get_parent(dev); + vmbus_get_crs(acpi0, dev); + + /* Try to locate pcib0 and find _CRS on it */ + if (device_get_children(acpi0, &children, &count) != 0) + return; + + for (i = 0; i < count; i++) { + if (!device_is_attached(children[i])) + continue; + + if (strcmp("pcib0", device_get_nameunit(children[i]))) + continue; + + pcib0 = children[i]; + break; + } + free(children, M_TEMP); + + if (pcib0) + vmbus_get_crs(pcib0, dev); +} + +static void +vmbus_free_mmio_list(device_t dev) +{ + struct vmbus_softc *sc = device_get_softc(dev); + struct vmbus_mmio_entry *e; + + while ((e = TAILQ_FIRST(&sc->vmbus_mmio_list)) != NULL) { + TAILQ_REMOVE(&sc->vmbus_mmio_list, e, link); + free(e, M_DEVBUF); + } +} + static int vmbus_probe(device_t dev) { @@ -1037,6 +1273,9 @@ if (sc->vmbus_flags & VMBUS_FLAG_ATTACHED) return (0); + + vmbus_get_mmio_list(sc->vmbus_dev); + sc->vmbus_flags |= VMBUS_FLAG_ATTACHED; sc->vmbus_gpadl = VMBUS_GPADL_START; @@ -1183,6 +1422,8 @@ mtx_destroy(&sc->vmbus_prichan_lock); mtx_destroy(&sc->vmbus_chan_lock); + vmbus_free_mmio_list(dev); + return (0); } Index: sys/dev/hyperv/vmbus/vmbus_var.h =================================================================== --- sys/dev/hyperv/vmbus/vmbus_var.h +++ sys/dev/hyperv/vmbus/vmbus_var.h @@ -77,6 +77,18 @@ struct task message_task; /* message task */ } __aligned(CACHE_LINE_SIZE); +#if __FreeBSD_version < 1100000 +typedef u_long rman_res_t; +#define RM_MAX_END (~(rman_res_t)0) +#define RMAN_IS_DEFAULT_RANGE(s,e) ((s) == 0 && (e) == RM_MAX_END) +#endif + +struct vmbus_mmio_entry { + TAILQ_ENTRY(vmbus_mmio_entry) link; + rman_res_t start; + rman_res_t end; +}; + struct vmbus_softc { void (*vmbus_event_proc)(struct vmbus_softc *, int); u_long *vmbus_tx_evtflags; @@ -120,6 +132,9 @@ /* Complete channel list */ struct mtx vmbus_chan_lock; TAILQ_HEAD(, vmbus_channel) vmbus_chans; + + /* The list of usable MMIO ranges for PCIe pass-through */ + TAILQ_HEAD(, vmbus_mmio_entry) vmbus_mmio_list; }; #define VMBUS_FLAG_ATTACHED 0x0001 /* vmbus was attached */