18c582c7cSDexuan Cui /*-
293b4e111SSepherosa Ziehau * Copyright (c) 2016-2017 Microsoft Corp.
38c582c7cSDexuan Cui * All rights reserved.
48c582c7cSDexuan Cui *
58c582c7cSDexuan Cui * Redistribution and use in source and binary forms, with or without
68c582c7cSDexuan Cui * modification, are permitted provided that the following conditions
78c582c7cSDexuan Cui * are met:
88c582c7cSDexuan Cui * 1. Redistributions of source code must retain the above copyright
98c582c7cSDexuan Cui * notice, this list of conditions and the following disclaimer.
108c582c7cSDexuan Cui * 2. Redistributions in binary form must reproduce the above copyright
118c582c7cSDexuan Cui * notice, this list of conditions and the following disclaimer in the
128c582c7cSDexuan Cui * documentation and/or other materials provided with the distribution.
138c582c7cSDexuan Cui *
148c582c7cSDexuan Cui * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
158c582c7cSDexuan Cui * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
168c582c7cSDexuan Cui * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
178c582c7cSDexuan Cui * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
188c582c7cSDexuan Cui * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
198c582c7cSDexuan Cui * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
208c582c7cSDexuan Cui * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
218c582c7cSDexuan Cui * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
228c582c7cSDexuan Cui * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
238c582c7cSDexuan Cui * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
248c582c7cSDexuan Cui * SUCH DAMAGE.
258c582c7cSDexuan Cui */
268c582c7cSDexuan Cui
278c582c7cSDexuan Cui #include <sys/cdefs.h>
286e5b082cSWei Hu #include "opt_acpi.h"
29cdb316eeSDexuan Cui
308c582c7cSDexuan Cui #include <sys/param.h>
318c582c7cSDexuan Cui #include <sys/systm.h>
328c582c7cSDexuan Cui #include <sys/types.h>
338c582c7cSDexuan Cui #include <sys/malloc.h>
348c582c7cSDexuan Cui #include <sys/module.h>
358c582c7cSDexuan Cui #include <sys/kernel.h>
368c582c7cSDexuan Cui #include <sys/queue.h>
378c582c7cSDexuan Cui #include <sys/lock.h>
388c582c7cSDexuan Cui #include <sys/sx.h>
398c582c7cSDexuan Cui #include <sys/smp.h>
408c582c7cSDexuan Cui #include <sys/sysctl.h>
418c582c7cSDexuan Cui #include <sys/bus.h>
428c582c7cSDexuan Cui #include <sys/rman.h>
438c582c7cSDexuan Cui #include <sys/mutex.h>
448c582c7cSDexuan Cui #include <sys/errno.h>
458c582c7cSDexuan Cui
468c582c7cSDexuan Cui #include <vm/vm.h>
478c582c7cSDexuan Cui #include <vm/vm_param.h>
488c582c7cSDexuan Cui #include <vm/vm_kern.h>
498c582c7cSDexuan Cui #include <vm/pmap.h>
508c582c7cSDexuan Cui
516e5b082cSWei Hu #if defined(__aarch64__)
526e5b082cSWei Hu #include <arm64/include/intr.h>
536e5b082cSWei Hu #endif
548c582c7cSDexuan Cui #include <machine/atomic.h>
558c582c7cSDexuan Cui #include <machine/bus.h>
568c582c7cSDexuan Cui #include <machine/frame.h>
578c582c7cSDexuan Cui #include <machine/pci_cfgreg.h>
588c582c7cSDexuan Cui #include <machine/resource.h>
598c582c7cSDexuan Cui
608c582c7cSDexuan Cui #include <sys/pciio.h>
618c582c7cSDexuan Cui #include <dev/pci/pcireg.h>
628c582c7cSDexuan Cui #include <dev/pci/pcivar.h>
638c582c7cSDexuan Cui #include <dev/pci/pci_private.h>
648c582c7cSDexuan Cui #include <dev/pci/pcib_private.h>
658c582c7cSDexuan Cui #include "pcib_if.h"
666e5b082cSWei Hu #if defined(__i386__) || defined(__amd64__)
678c582c7cSDexuan Cui #include <machine/intr_machdep.h>
688c582c7cSDexuan Cui #include <x86/apicreg.h>
69999174baSWei Hu #include <x86/apicvar.h>
706e5b082cSWei Hu #endif
716e5b082cSWei Hu #if defined(__aarch64__)
726e5b082cSWei Hu #include <contrib/dev/acpica/include/acpi.h>
736e5b082cSWei Hu #include <contrib/dev/acpica/include/accommon.h>
746e5b082cSWei Hu #include <dev/acpica/acpivar.h>
756e5b082cSWei Hu #include <dev/acpica/acpi_pcibvar.h>
766e5b082cSWei Hu #endif
778c582c7cSDexuan Cui #include <dev/hyperv/include/hyperv.h>
788c582c7cSDexuan Cui #include <dev/hyperv/include/vmbus_xact.h>
798c582c7cSDexuan Cui #include <dev/hyperv/vmbus/vmbus_reg.h>
808c582c7cSDexuan Cui #include <dev/hyperv/vmbus/vmbus_chanvar.h>
818c582c7cSDexuan Cui
828c582c7cSDexuan Cui #include "vmbus_if.h"
838c582c7cSDexuan Cui
848c582c7cSDexuan Cui struct completion {
858c582c7cSDexuan Cui unsigned int done;
868c582c7cSDexuan Cui struct mtx lock;
878c582c7cSDexuan Cui };
888c582c7cSDexuan Cui
898c582c7cSDexuan Cui static void
init_completion(struct completion * c)908c582c7cSDexuan Cui init_completion(struct completion *c)
918c582c7cSDexuan Cui {
928c582c7cSDexuan Cui memset(c, 0, sizeof(*c));
938c582c7cSDexuan Cui mtx_init(&c->lock, "hvcmpl", NULL, MTX_DEF);
948c582c7cSDexuan Cui c->done = 0;
958c582c7cSDexuan Cui }
96ea11861eSWei Hu static void
reinit_completion(struct completion * c)97ea11861eSWei Hu reinit_completion(struct completion *c)
98ea11861eSWei Hu {
99ea11861eSWei Hu c->done = 0;
100ea11861eSWei Hu }
1018c582c7cSDexuan Cui static void
free_completion(struct completion * c)1028c582c7cSDexuan Cui free_completion(struct completion *c)
1038c582c7cSDexuan Cui {
1048c582c7cSDexuan Cui mtx_destroy(&c->lock);
1058c582c7cSDexuan Cui }
1068c582c7cSDexuan Cui
1078c582c7cSDexuan Cui static void
complete(struct completion * c)1088c582c7cSDexuan Cui complete(struct completion *c)
1098c582c7cSDexuan Cui {
1108c582c7cSDexuan Cui mtx_lock(&c->lock);
1118c582c7cSDexuan Cui c->done++;
1128c582c7cSDexuan Cui mtx_unlock(&c->lock);
1138c582c7cSDexuan Cui wakeup(c);
1148c582c7cSDexuan Cui }
1158c582c7cSDexuan Cui
1168c582c7cSDexuan Cui static void
wait_for_completion(struct completion * c)1178c582c7cSDexuan Cui wait_for_completion(struct completion *c)
1188c582c7cSDexuan Cui {
1198c582c7cSDexuan Cui mtx_lock(&c->lock);
1208c582c7cSDexuan Cui while (c->done == 0)
1218c582c7cSDexuan Cui mtx_sleep(c, &c->lock, 0, "hvwfc", 0);
1228c582c7cSDexuan Cui c->done--;
1238c582c7cSDexuan Cui mtx_unlock(&c->lock);
1248c582c7cSDexuan Cui }
1258c582c7cSDexuan Cui
12675c2786cSWei Hu /*
12775c2786cSWei Hu * Return: 0 if completed, a non-zero value if timed out.
12875c2786cSWei Hu */
12975c2786cSWei Hu static int
wait_for_completion_timeout(struct completion * c,int timeout)13075c2786cSWei Hu wait_for_completion_timeout(struct completion *c, int timeout)
13175c2786cSWei Hu {
13275c2786cSWei Hu int ret;
13375c2786cSWei Hu
13475c2786cSWei Hu mtx_lock(&c->lock);
13575c2786cSWei Hu
13675c2786cSWei Hu if (c->done == 0)
13775c2786cSWei Hu mtx_sleep(c, &c->lock, 0, "hvwfc", timeout);
13875c2786cSWei Hu
13975c2786cSWei Hu if (c->done > 0) {
14075c2786cSWei Hu c->done--;
14175c2786cSWei Hu ret = 0;
14275c2786cSWei Hu } else {
14375c2786cSWei Hu ret = 1;
14475c2786cSWei Hu }
14575c2786cSWei Hu
14675c2786cSWei Hu mtx_unlock(&c->lock);
14775c2786cSWei Hu
14875c2786cSWei Hu return (ret);
14975c2786cSWei Hu }
15075c2786cSWei Hu
151ea11861eSWei Hu #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
1526e5b082cSWei Hu #define PCI_MAKE_VERSION(major, minor) ((uint32_t)(((major) << 16) | (minor)))
1538c582c7cSDexuan Cui
154ea11861eSWei Hu enum pci_protocol_version_t {
1558c582c7cSDexuan Cui PCI_PROTOCOL_VERSION_1_1 = PCI_MAKE_VERSION(1, 1),
156ea11861eSWei Hu PCI_PROTOCOL_VERSION_1_4 = PCI_MAKE_VERSION(1, 4),
157ea11861eSWei Hu };
158ea11861eSWei Hu
159ea11861eSWei Hu static enum pci_protocol_version_t pci_protocol_versions[] = {
160ea11861eSWei Hu PCI_PROTOCOL_VERSION_1_4,
161ea11861eSWei Hu PCI_PROTOCOL_VERSION_1_1,
1628c582c7cSDexuan Cui };
1638c582c7cSDexuan Cui
1648c582c7cSDexuan Cui #define PCI_CONFIG_MMIO_LENGTH 0x2000
1658c582c7cSDexuan Cui #define CFG_PAGE_OFFSET 0x1000
1668c582c7cSDexuan Cui #define CFG_PAGE_SIZE (PCI_CONFIG_MMIO_LENGTH - CFG_PAGE_OFFSET)
1678c582c7cSDexuan Cui
1688c582c7cSDexuan Cui /*
1698c582c7cSDexuan Cui * Message Types
1708c582c7cSDexuan Cui */
1718c582c7cSDexuan Cui
1728c582c7cSDexuan Cui enum pci_message_type {
1738c582c7cSDexuan Cui /*
1748c582c7cSDexuan Cui * Version 1.1
1758c582c7cSDexuan Cui */
1768c582c7cSDexuan Cui PCI_MESSAGE_BASE = 0x42490000,
1778c582c7cSDexuan Cui PCI_BUS_RELATIONS = PCI_MESSAGE_BASE + 0,
1788c582c7cSDexuan Cui PCI_QUERY_BUS_RELATIONS = PCI_MESSAGE_BASE + 1,
1798c582c7cSDexuan Cui PCI_POWER_STATE_CHANGE = PCI_MESSAGE_BASE + 4,
1808c582c7cSDexuan Cui PCI_QUERY_RESOURCE_REQUIREMENTS = PCI_MESSAGE_BASE + 5,
1818c582c7cSDexuan Cui PCI_QUERY_RESOURCE_RESOURCES = PCI_MESSAGE_BASE + 6,
1828c582c7cSDexuan Cui PCI_BUS_D0ENTRY = PCI_MESSAGE_BASE + 7,
1838c582c7cSDexuan Cui PCI_BUS_D0EXIT = PCI_MESSAGE_BASE + 8,
1848c582c7cSDexuan Cui PCI_READ_BLOCK = PCI_MESSAGE_BASE + 9,
1858c582c7cSDexuan Cui PCI_WRITE_BLOCK = PCI_MESSAGE_BASE + 0xA,
1868c582c7cSDexuan Cui PCI_EJECT = PCI_MESSAGE_BASE + 0xB,
1878c582c7cSDexuan Cui PCI_QUERY_STOP = PCI_MESSAGE_BASE + 0xC,
1888c582c7cSDexuan Cui PCI_REENABLE = PCI_MESSAGE_BASE + 0xD,
1898c582c7cSDexuan Cui PCI_QUERY_STOP_FAILED = PCI_MESSAGE_BASE + 0xE,
1908c582c7cSDexuan Cui PCI_EJECTION_COMPLETE = PCI_MESSAGE_BASE + 0xF,
1918c582c7cSDexuan Cui PCI_RESOURCES_ASSIGNED = PCI_MESSAGE_BASE + 0x10,
1928c582c7cSDexuan Cui PCI_RESOURCES_RELEASED = PCI_MESSAGE_BASE + 0x11,
1938c582c7cSDexuan Cui PCI_INVALIDATE_BLOCK = PCI_MESSAGE_BASE + 0x12,
1948c582c7cSDexuan Cui PCI_QUERY_PROTOCOL_VERSION = PCI_MESSAGE_BASE + 0x13,
1958c582c7cSDexuan Cui PCI_CREATE_INTERRUPT_MESSAGE = PCI_MESSAGE_BASE + 0x14,
1968c582c7cSDexuan Cui PCI_DELETE_INTERRUPT_MESSAGE = PCI_MESSAGE_BASE + 0x15,
1976e5b082cSWei Hu PCI_RESOURCES_ASSIGNED2 = PCI_MESSAGE_BASE + 0x16,
1986e5b082cSWei Hu PCI_CREATE_INTERRUPT_MESSAGE2 = PCI_MESSAGE_BASE + 0x17,
1996e5b082cSWei Hu PCI_DELETE_INTERRUPT_MESSAGE2 = PCI_MESSAGE_BASE + 0x18, /* unused */
2006e5b082cSWei Hu PCI_BUS_RELATIONS2 = PCI_MESSAGE_BASE + 0x19,
2016e5b082cSWei Hu PCI_RESOURCES_ASSIGNED3 = PCI_MESSAGE_BASE + 0x1A,
2026e5b082cSWei Hu PCI_CREATE_INTERRUPT_MESSAGE3 = PCI_MESSAGE_BASE + 0x1B,
2038c582c7cSDexuan Cui PCI_MESSAGE_MAXIMUM
2048c582c7cSDexuan Cui };
2058c582c7cSDexuan Cui
206ea11861eSWei Hu #define STATUS_REVISION_MISMATCH 0xC0000059
207ea11861eSWei Hu
2088c582c7cSDexuan Cui /*
2098c582c7cSDexuan Cui * Structures defining the virtual PCI Express protocol.
2108c582c7cSDexuan Cui */
2118c582c7cSDexuan Cui
2128c582c7cSDexuan Cui union pci_version {
2138c582c7cSDexuan Cui struct {
2148c582c7cSDexuan Cui uint16_t minor_version;
2158c582c7cSDexuan Cui uint16_t major_version;
2168c582c7cSDexuan Cui } parts;
2178c582c7cSDexuan Cui uint32_t version;
2188c582c7cSDexuan Cui } __packed;
2198c582c7cSDexuan Cui
2208c582c7cSDexuan Cui /*
2218c582c7cSDexuan Cui * This representation is the one used in Windows, which is
2228c582c7cSDexuan Cui * what is expected when sending this back and forth with
2238c582c7cSDexuan Cui * the Hyper-V parent partition.
2248c582c7cSDexuan Cui */
2258c582c7cSDexuan Cui union win_slot_encoding {
2268c582c7cSDexuan Cui struct {
2278c582c7cSDexuan Cui uint32_t slot:5;
2288c582c7cSDexuan Cui uint32_t func:3;
2298c582c7cSDexuan Cui uint32_t reserved:24;
2308c582c7cSDexuan Cui } bits;
2318c582c7cSDexuan Cui uint32_t val;
2328c582c7cSDexuan Cui } __packed;
2338c582c7cSDexuan Cui
2348c582c7cSDexuan Cui struct pci_func_desc {
2358c582c7cSDexuan Cui uint16_t v_id; /* vendor ID */
2368c582c7cSDexuan Cui uint16_t d_id; /* device ID */
2378c582c7cSDexuan Cui uint8_t rev;
2388c582c7cSDexuan Cui uint8_t prog_intf;
2398c582c7cSDexuan Cui uint8_t subclass;
2408c582c7cSDexuan Cui uint8_t base_class;
2418c582c7cSDexuan Cui uint32_t subsystem_id;
2428c582c7cSDexuan Cui union win_slot_encoding wslot;
2438c582c7cSDexuan Cui uint32_t ser; /* serial number */
2448c582c7cSDexuan Cui } __packed;
2458c582c7cSDexuan Cui
2466e5b082cSWei Hu struct pci_func_desc2 {
2476e5b082cSWei Hu uint16_t v_id; /* vendor ID */
2486e5b082cSWei Hu uint16_t d_id; /* device ID */
2496e5b082cSWei Hu uint8_t rev;
2506e5b082cSWei Hu uint8_t prog_intf;
2516e5b082cSWei Hu uint8_t subclass;
2526e5b082cSWei Hu uint8_t base_class;
2536e5b082cSWei Hu uint32_t subsystem_id;
2546e5b082cSWei Hu union win_slot_encoding wslot;
2556e5b082cSWei Hu uint32_t ser; /* serial number */
2566e5b082cSWei Hu uint32_t flags;
2576e5b082cSWei Hu uint16_t virtual_numa_node;
2586e5b082cSWei Hu uint16_t reserved;
2596e5b082cSWei Hu } __packed;
2606e5b082cSWei Hu
2616e5b082cSWei Hu
2628c582c7cSDexuan Cui struct hv_msi_desc {
2638c582c7cSDexuan Cui uint8_t vector;
2648c582c7cSDexuan Cui uint8_t delivery_mode;
2658c582c7cSDexuan Cui uint16_t vector_count;
2668c582c7cSDexuan Cui uint32_t reserved;
2678c582c7cSDexuan Cui uint64_t cpu_mask;
2688c582c7cSDexuan Cui } __packed;
2698c582c7cSDexuan Cui
2706e5b082cSWei Hu struct hv_msi_desc3 {
2716e5b082cSWei Hu uint32_t vector;
2726e5b082cSWei Hu uint8_t delivery_mode;
2736e5b082cSWei Hu uint8_t reserved;
2746e5b082cSWei Hu uint16_t vector_count;
2756e5b082cSWei Hu uint16_t processor_count;
2766e5b082cSWei Hu uint16_t processor_array[32];
2776e5b082cSWei Hu } __packed;
2786e5b082cSWei Hu
2798c582c7cSDexuan Cui struct tran_int_desc {
2808c582c7cSDexuan Cui uint16_t reserved;
2818c582c7cSDexuan Cui uint16_t vector_count;
2828c582c7cSDexuan Cui uint32_t data;
2838c582c7cSDexuan Cui uint64_t address;
2848c582c7cSDexuan Cui } __packed;
2858c582c7cSDexuan Cui
2868c582c7cSDexuan Cui struct pci_message {
2878c582c7cSDexuan Cui uint32_t type;
2888c582c7cSDexuan Cui } __packed;
2898c582c7cSDexuan Cui
2908c582c7cSDexuan Cui struct pci_child_message {
2918c582c7cSDexuan Cui struct pci_message message_type;
2928c582c7cSDexuan Cui union win_slot_encoding wslot;
2938c582c7cSDexuan Cui } __packed;
2948c582c7cSDexuan Cui
2958c582c7cSDexuan Cui struct pci_incoming_message {
2968c582c7cSDexuan Cui struct vmbus_chanpkt_hdr hdr;
2978c582c7cSDexuan Cui struct pci_message message_type;
2988c582c7cSDexuan Cui } __packed;
2998c582c7cSDexuan Cui
3008c582c7cSDexuan Cui struct pci_response {
3018c582c7cSDexuan Cui struct vmbus_chanpkt_hdr hdr;
3028c582c7cSDexuan Cui int32_t status; /* negative values are failures */
3038c582c7cSDexuan Cui } __packed;
3048c582c7cSDexuan Cui
3058c582c7cSDexuan Cui struct pci_packet {
3068c582c7cSDexuan Cui void (*completion_func)(void *context, struct pci_response *resp,
3078c582c7cSDexuan Cui int resp_packet_size);
3088c582c7cSDexuan Cui void *compl_ctxt;
3098c582c7cSDexuan Cui
3108c582c7cSDexuan Cui struct pci_message message[0];
3118c582c7cSDexuan Cui };
3128c582c7cSDexuan Cui
3138c582c7cSDexuan Cui /*
3148c582c7cSDexuan Cui * Specific message types supporting the PCI protocol.
3158c582c7cSDexuan Cui */
3168c582c7cSDexuan Cui
3178c582c7cSDexuan Cui struct pci_version_request {
3188c582c7cSDexuan Cui struct pci_message message_type;
3198c582c7cSDexuan Cui uint32_t protocol_version;
3208c582c7cSDexuan Cui uint32_t reservedz:31;
3218c582c7cSDexuan Cui } __packed;
3228c582c7cSDexuan Cui
3238c582c7cSDexuan Cui struct pci_bus_d0_entry {
3248c582c7cSDexuan Cui struct pci_message message_type;
3258c582c7cSDexuan Cui uint32_t reserved;
3268c582c7cSDexuan Cui uint64_t mmio_base;
3278c582c7cSDexuan Cui } __packed;
3288c582c7cSDexuan Cui
3298c582c7cSDexuan Cui struct pci_bus_relations {
3308c582c7cSDexuan Cui struct pci_incoming_message incoming;
3318c582c7cSDexuan Cui uint32_t device_count;
3328c582c7cSDexuan Cui struct pci_func_desc func[0];
3338c582c7cSDexuan Cui } __packed;
3348c582c7cSDexuan Cui
3356e5b082cSWei Hu struct pci_bus_relations2 {
3366e5b082cSWei Hu struct pci_incoming_message incoming;
3376e5b082cSWei Hu uint32_t device_count;
3386e5b082cSWei Hu struct pci_func_desc2 func[0];
3396e5b082cSWei Hu } __packed;
3406e5b082cSWei Hu
3418c582c7cSDexuan Cui #define MAX_NUM_BARS (PCIR_MAX_BAR_0 + 1)
3428c582c7cSDexuan Cui struct pci_q_res_req_response {
3438c582c7cSDexuan Cui struct vmbus_chanpkt_hdr hdr;
3448c582c7cSDexuan Cui int32_t status; /* negative values are failures */
3458c582c7cSDexuan Cui uint32_t probed_bar[MAX_NUM_BARS];
3468c582c7cSDexuan Cui } __packed;
3478c582c7cSDexuan Cui
3488c582c7cSDexuan Cui struct pci_resources_assigned {
3498c582c7cSDexuan Cui struct pci_message message_type;
3508c582c7cSDexuan Cui union win_slot_encoding wslot;
3518c582c7cSDexuan Cui uint8_t memory_range[0x14][MAX_NUM_BARS]; /* unused here */
3528c582c7cSDexuan Cui uint32_t msi_descriptors;
3538c582c7cSDexuan Cui uint32_t reserved[4];
3548c582c7cSDexuan Cui } __packed;
3558c582c7cSDexuan Cui
3566e5b082cSWei Hu struct pci_resources_assigned2 {
3576e5b082cSWei Hu struct pci_message message_type;
3586e5b082cSWei Hu union win_slot_encoding wslot;
3596e5b082cSWei Hu uint8_t memory_range[0x14][6]; /* not used here */
3606e5b082cSWei Hu uint32_t msi_descriptor_count;
3616e5b082cSWei Hu uint8_t reserved[70];
3626e5b082cSWei Hu } __packed;
3636e5b082cSWei Hu
3648c582c7cSDexuan Cui struct pci_create_interrupt {
3658c582c7cSDexuan Cui struct pci_message message_type;
3668c582c7cSDexuan Cui union win_slot_encoding wslot;
3678c582c7cSDexuan Cui struct hv_msi_desc int_desc;
3688c582c7cSDexuan Cui } __packed;
3698c582c7cSDexuan Cui
3706e5b082cSWei Hu struct pci_create_interrupt3 {
3716e5b082cSWei Hu struct pci_message message_type;
3726e5b082cSWei Hu union win_slot_encoding wslot;
3736e5b082cSWei Hu struct hv_msi_desc3 int_desc;
3746e5b082cSWei Hu } __packed;
3756e5b082cSWei Hu
3768c582c7cSDexuan Cui struct pci_create_int_response {
3778c582c7cSDexuan Cui struct pci_response response;
3788c582c7cSDexuan Cui uint32_t reserved;
3798c582c7cSDexuan Cui struct tran_int_desc int_desc;
3808c582c7cSDexuan Cui } __packed;
3818c582c7cSDexuan Cui
3828c582c7cSDexuan Cui struct pci_delete_interrupt {
3838c582c7cSDexuan Cui struct pci_message message_type;
3848c582c7cSDexuan Cui union win_slot_encoding wslot;
3858c582c7cSDexuan Cui struct tran_int_desc int_desc;
3868c582c7cSDexuan Cui } __packed;
3878c582c7cSDexuan Cui
3888c582c7cSDexuan Cui struct pci_dev_incoming {
3898c582c7cSDexuan Cui struct pci_incoming_message incoming;
3908c582c7cSDexuan Cui union win_slot_encoding wslot;
3918c582c7cSDexuan Cui } __packed;
3928c582c7cSDexuan Cui
3938c582c7cSDexuan Cui struct pci_eject_response {
3948c582c7cSDexuan Cui struct pci_message message_type;
3958c582c7cSDexuan Cui union win_slot_encoding wslot;
3968c582c7cSDexuan Cui uint32_t status;
3978c582c7cSDexuan Cui } __packed;
3988c582c7cSDexuan Cui
3998c582c7cSDexuan Cui /*
4008c582c7cSDexuan Cui * Driver specific state.
4018c582c7cSDexuan Cui */
4028c582c7cSDexuan Cui
4038c582c7cSDexuan Cui enum hv_pcibus_state {
4048c582c7cSDexuan Cui hv_pcibus_init = 0,
4058c582c7cSDexuan Cui hv_pcibus_installed,
4068c582c7cSDexuan Cui };
4078c582c7cSDexuan Cui
4088c582c7cSDexuan Cui struct hv_pcibus {
4098c582c7cSDexuan Cui device_t pcib;
4108c582c7cSDexuan Cui device_t pci_bus;
4118c582c7cSDexuan Cui struct vmbus_pcib_softc *sc;
4128c582c7cSDexuan Cui
4138c582c7cSDexuan Cui uint16_t pci_domain;
4148c582c7cSDexuan Cui
4158c582c7cSDexuan Cui enum hv_pcibus_state state;
4168c582c7cSDexuan Cui
4178c582c7cSDexuan Cui struct resource *cfg_res;
4188c582c7cSDexuan Cui
4198c582c7cSDexuan Cui struct completion query_completion, *query_comp;
4208c582c7cSDexuan Cui
4218c582c7cSDexuan Cui struct mtx config_lock; /* Avoid two threads writing index page */
4228c582c7cSDexuan Cui struct mtx device_list_lock; /* Protect lists below */
4236e5b082cSWei Hu uint32_t protocol_version;
4248c582c7cSDexuan Cui TAILQ_HEAD(, hv_pci_dev) children;
4258c582c7cSDexuan Cui TAILQ_HEAD(, hv_dr_state) dr_list;
4268c582c7cSDexuan Cui
4278c582c7cSDexuan Cui volatile int detaching;
4288c582c7cSDexuan Cui };
4298c582c7cSDexuan Cui
4306e5b082cSWei Hu struct hv_pcidev_desc {
4316e5b082cSWei Hu uint16_t v_id; /* vendor ID */
4326e5b082cSWei Hu uint16_t d_id; /* device ID */
4336e5b082cSWei Hu uint8_t rev;
4346e5b082cSWei Hu uint8_t prog_intf;
4356e5b082cSWei Hu uint8_t subclass;
4366e5b082cSWei Hu uint8_t base_class;
4376e5b082cSWei Hu uint32_t subsystem_id;
4386e5b082cSWei Hu union win_slot_encoding wslot;
4396e5b082cSWei Hu uint32_t ser; /* serial number */
4406e5b082cSWei Hu uint32_t flags;
4416e5b082cSWei Hu uint16_t virtual_numa_node;
4426e5b082cSWei Hu } __packed;
4436e5b082cSWei Hu
4448c582c7cSDexuan Cui struct hv_pci_dev {
4458c582c7cSDexuan Cui TAILQ_ENTRY(hv_pci_dev) link;
4468c582c7cSDexuan Cui
447ea11861eSWei Hu struct hv_pcidev_desc desc;
4488c582c7cSDexuan Cui
4498c582c7cSDexuan Cui bool reported_missing;
4508c582c7cSDexuan Cui
4518c582c7cSDexuan Cui struct hv_pcibus *hbus;
4528c582c7cSDexuan Cui struct task eject_task;
4538c582c7cSDexuan Cui
4548c582c7cSDexuan Cui TAILQ_HEAD(, hv_irq_desc) irq_desc_list;
4558c582c7cSDexuan Cui
4568c582c7cSDexuan Cui /*
4578c582c7cSDexuan Cui * What would be observed if one wrote 0xFFFFFFFF to a BAR and then
4588c582c7cSDexuan Cui * read it back, for each of the BAR offsets within config space.
4598c582c7cSDexuan Cui */
4608c582c7cSDexuan Cui uint32_t probed_bar[MAX_NUM_BARS];
4618c582c7cSDexuan Cui };
4628c582c7cSDexuan Cui
4638c582c7cSDexuan Cui /*
4648c582c7cSDexuan Cui * Tracks "Device Relations" messages from the host, which must be both
4658c582c7cSDexuan Cui * processed in order.
4668c582c7cSDexuan Cui */
4678c582c7cSDexuan Cui struct hv_dr_work {
4688c582c7cSDexuan Cui struct task task;
4698c582c7cSDexuan Cui struct hv_pcibus *bus;
4708c582c7cSDexuan Cui };
4718c582c7cSDexuan Cui
4728c582c7cSDexuan Cui struct hv_dr_state {
4738c582c7cSDexuan Cui TAILQ_ENTRY(hv_dr_state) link;
4748c582c7cSDexuan Cui uint32_t device_count;
475ea11861eSWei Hu struct hv_pcidev_desc func[0];
4768c582c7cSDexuan Cui };
4778c582c7cSDexuan Cui
4788c582c7cSDexuan Cui struct hv_irq_desc {
4798c582c7cSDexuan Cui TAILQ_ENTRY(hv_irq_desc) link;
4808c582c7cSDexuan Cui struct tran_int_desc desc;
4818c582c7cSDexuan Cui int irq;
4828c582c7cSDexuan Cui };
4838c582c7cSDexuan Cui
4848c582c7cSDexuan Cui #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
4858c582c7cSDexuan Cui #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
4868c582c7cSDexuan Cui #define PCI_FUNC(devfn) ((devfn) & 0x07)
4878c582c7cSDexuan Cui
4888c582c7cSDexuan Cui static uint32_t
devfn_to_wslot(unsigned int devfn)4898c582c7cSDexuan Cui devfn_to_wslot(unsigned int devfn)
4908c582c7cSDexuan Cui {
4918c582c7cSDexuan Cui union win_slot_encoding wslot;
4928c582c7cSDexuan Cui
4938c582c7cSDexuan Cui wslot.val = 0;
4948c582c7cSDexuan Cui wslot.bits.slot = PCI_SLOT(devfn);
4958c582c7cSDexuan Cui wslot.bits.func = PCI_FUNC(devfn);
4968c582c7cSDexuan Cui
4978c582c7cSDexuan Cui return (wslot.val);
4988c582c7cSDexuan Cui }
4998c582c7cSDexuan Cui
5008c582c7cSDexuan Cui static unsigned int
wslot_to_devfn(uint32_t wslot)5018c582c7cSDexuan Cui wslot_to_devfn(uint32_t wslot)
5028c582c7cSDexuan Cui {
5038c582c7cSDexuan Cui union win_slot_encoding encoding;
5048c582c7cSDexuan Cui unsigned int slot;
5058c582c7cSDexuan Cui unsigned int func;
5068c582c7cSDexuan Cui
5078c582c7cSDexuan Cui encoding.val = wslot;
5088c582c7cSDexuan Cui
5098c582c7cSDexuan Cui slot = encoding.bits.slot;
5108c582c7cSDexuan Cui func = encoding.bits.func;
5118c582c7cSDexuan Cui
5128c582c7cSDexuan Cui return (PCI_DEVFN(slot, func));
5138c582c7cSDexuan Cui }
5148c582c7cSDexuan Cui
5158c582c7cSDexuan Cui struct vmbus_pcib_softc {
5168c582c7cSDexuan Cui struct vmbus_channel *chan;
5178c582c7cSDexuan Cui void *rx_buf;
5188c582c7cSDexuan Cui
5198c582c7cSDexuan Cui struct taskqueue *taskq;
5208c582c7cSDexuan Cui
5218c582c7cSDexuan Cui struct hv_pcibus *hbus;
5228c582c7cSDexuan Cui };
5238c582c7cSDexuan Cui
5248c582c7cSDexuan Cui /* {44C4F61D-4444-4400-9D52-802E27EDE19F} */
5258c582c7cSDexuan Cui static const struct hyperv_guid g_pass_through_dev_type = {
5268c582c7cSDexuan Cui .hv_guid = {0x1D, 0xF6, 0xC4, 0x44, 0x44, 0x44, 0x00, 0x44,
5278c582c7cSDexuan Cui 0x9D, 0x52, 0x80, 0x2E, 0x27, 0xED, 0xE1, 0x9F}
5288c582c7cSDexuan Cui };
5298c582c7cSDexuan Cui
5308c582c7cSDexuan Cui struct hv_pci_compl {
5318c582c7cSDexuan Cui struct completion host_event;
5328c582c7cSDexuan Cui int32_t completion_status;
5338c582c7cSDexuan Cui };
5348c582c7cSDexuan Cui
5358c582c7cSDexuan Cui struct q_res_req_compl {
5368c582c7cSDexuan Cui struct completion host_event;
5378c582c7cSDexuan Cui struct hv_pci_dev *hpdev;
5388c582c7cSDexuan Cui };
5398c582c7cSDexuan Cui
5408c582c7cSDexuan Cui struct compose_comp_ctxt {
5418c582c7cSDexuan Cui struct hv_pci_compl comp_pkt;
5428c582c7cSDexuan Cui struct tran_int_desc int_desc;
5438c582c7cSDexuan Cui };
5448c582c7cSDexuan Cui
54575c2786cSWei Hu /*
54675c2786cSWei Hu * It is possible the device is revoked during initialization.
54775c2786cSWei Hu * Check if this happens during wait.
54875c2786cSWei Hu * Return: 0 if response arrived, ENODEV if device revoked.
54975c2786cSWei Hu */
55075c2786cSWei Hu static int
wait_for_response(struct hv_pcibus * hbus,struct completion * c)55175c2786cSWei Hu wait_for_response(struct hv_pcibus *hbus, struct completion *c)
55275c2786cSWei Hu {
55375c2786cSWei Hu do {
55475c2786cSWei Hu if (vmbus_chan_is_revoked(hbus->sc->chan)) {
55575c2786cSWei Hu device_printf(hbus->pcib,
55675c2786cSWei Hu "The device is revoked.\n");
55775c2786cSWei Hu return (ENODEV);
55875c2786cSWei Hu }
55975c2786cSWei Hu } while (wait_for_completion_timeout(c, hz /10) != 0);
56075c2786cSWei Hu
56175c2786cSWei Hu return 0;
56275c2786cSWei Hu }
56375c2786cSWei Hu
5648c582c7cSDexuan Cui static void
hv_pci_generic_compl(void * context,struct pci_response * resp,int resp_packet_size)5658c582c7cSDexuan Cui hv_pci_generic_compl(void *context, struct pci_response *resp,
5668c582c7cSDexuan Cui int resp_packet_size)
5678c582c7cSDexuan Cui {
5688c582c7cSDexuan Cui struct hv_pci_compl *comp_pkt = context;
5698c582c7cSDexuan Cui
5708c582c7cSDexuan Cui if (resp_packet_size >= sizeof(struct pci_response))
5718c582c7cSDexuan Cui comp_pkt->completion_status = resp->status;
5728c582c7cSDexuan Cui else
5738c582c7cSDexuan Cui comp_pkt->completion_status = -1;
5748c582c7cSDexuan Cui
5758c582c7cSDexuan Cui complete(&comp_pkt->host_event);
5768c582c7cSDexuan Cui }
5778c582c7cSDexuan Cui
5788c582c7cSDexuan Cui static void
q_resource_requirements(void * context,struct pci_response * resp,int resp_packet_size)5798c582c7cSDexuan Cui q_resource_requirements(void *context, struct pci_response *resp,
5808c582c7cSDexuan Cui int resp_packet_size)
5818c582c7cSDexuan Cui {
5828c582c7cSDexuan Cui struct q_res_req_compl *completion = context;
5838c582c7cSDexuan Cui struct pci_q_res_req_response *q_res_req =
5848c582c7cSDexuan Cui (struct pci_q_res_req_response *)resp;
5858c582c7cSDexuan Cui int i;
5868c582c7cSDexuan Cui
5878c582c7cSDexuan Cui if (resp->status < 0) {
5888c582c7cSDexuan Cui printf("vmbus_pcib: failed to query resource requirements\n");
5898c582c7cSDexuan Cui } else {
5908c582c7cSDexuan Cui for (i = 0; i < MAX_NUM_BARS; i++)
5918c582c7cSDexuan Cui completion->hpdev->probed_bar[i] =
5928c582c7cSDexuan Cui q_res_req->probed_bar[i];
5938c582c7cSDexuan Cui }
5948c582c7cSDexuan Cui
5958c582c7cSDexuan Cui complete(&completion->host_event);
5968c582c7cSDexuan Cui }
5978c582c7cSDexuan Cui
5988c582c7cSDexuan Cui static void
hv_pci_compose_compl(void * context,struct pci_response * resp,int resp_packet_size)5998c582c7cSDexuan Cui hv_pci_compose_compl(void *context, struct pci_response *resp,
6008c582c7cSDexuan Cui int resp_packet_size)
6018c582c7cSDexuan Cui {
6028c582c7cSDexuan Cui struct compose_comp_ctxt *comp_pkt = context;
6038c582c7cSDexuan Cui struct pci_create_int_response *int_resp =
6048c582c7cSDexuan Cui (struct pci_create_int_response *)resp;
6058c582c7cSDexuan Cui
6068c582c7cSDexuan Cui comp_pkt->comp_pkt.completion_status = resp->status;
6078c582c7cSDexuan Cui comp_pkt->int_desc = int_resp->int_desc;
6088c582c7cSDexuan Cui complete(&comp_pkt->comp_pkt.host_event);
6098c582c7cSDexuan Cui }
6108c582c7cSDexuan Cui
6118c582c7cSDexuan Cui static void
hv_int_desc_free(struct hv_pci_dev * hpdev,struct hv_irq_desc * hid)6128c582c7cSDexuan Cui hv_int_desc_free(struct hv_pci_dev *hpdev, struct hv_irq_desc *hid)
6138c582c7cSDexuan Cui {
6148c582c7cSDexuan Cui struct pci_delete_interrupt *int_pkt;
6158c582c7cSDexuan Cui struct {
6168c582c7cSDexuan Cui struct pci_packet pkt;
6178c582c7cSDexuan Cui uint8_t buffer[sizeof(struct pci_delete_interrupt)];
6188c582c7cSDexuan Cui } ctxt;
6198c582c7cSDexuan Cui
6208c582c7cSDexuan Cui memset(&ctxt, 0, sizeof(ctxt));
6218c582c7cSDexuan Cui int_pkt = (struct pci_delete_interrupt *)&ctxt.pkt.message;
6228c582c7cSDexuan Cui int_pkt->message_type.type = PCI_DELETE_INTERRUPT_MESSAGE;
6238c582c7cSDexuan Cui int_pkt->wslot.val = hpdev->desc.wslot.val;
6248c582c7cSDexuan Cui int_pkt->int_desc = hid->desc;
6258c582c7cSDexuan Cui
6268c582c7cSDexuan Cui vmbus_chan_send(hpdev->hbus->sc->chan, VMBUS_CHANPKT_TYPE_INBAND, 0,
6278c582c7cSDexuan Cui int_pkt, sizeof(*int_pkt), 0);
6288c582c7cSDexuan Cui
6298c582c7cSDexuan Cui free(hid, M_DEVBUF);
6308c582c7cSDexuan Cui }
6318c582c7cSDexuan Cui
6328c582c7cSDexuan Cui static void
hv_pci_delete_device(struct hv_pci_dev * hpdev)6338c582c7cSDexuan Cui hv_pci_delete_device(struct hv_pci_dev *hpdev)
6348c582c7cSDexuan Cui {
6358c582c7cSDexuan Cui struct hv_pcibus *hbus = hpdev->hbus;
6368c582c7cSDexuan Cui struct hv_irq_desc *hid, *tmp_hid;
6378c582c7cSDexuan Cui device_t pci_dev;
6388c582c7cSDexuan Cui int devfn;
6398c582c7cSDexuan Cui
6408c582c7cSDexuan Cui devfn = wslot_to_devfn(hpdev->desc.wslot.val);
6418c582c7cSDexuan Cui
642c6df6f53SWarner Losh bus_topo_lock();
6438c582c7cSDexuan Cui
6448c582c7cSDexuan Cui pci_dev = pci_find_dbsf(hbus->pci_domain,
6458c582c7cSDexuan Cui 0, PCI_SLOT(devfn), PCI_FUNC(devfn));
6468c582c7cSDexuan Cui if (pci_dev)
6478c582c7cSDexuan Cui device_delete_child(hbus->pci_bus, pci_dev);
6488c582c7cSDexuan Cui
649c6df6f53SWarner Losh bus_topo_unlock();
6508c582c7cSDexuan Cui
6518c582c7cSDexuan Cui mtx_lock(&hbus->device_list_lock);
6528c582c7cSDexuan Cui TAILQ_REMOVE(&hbus->children, hpdev, link);
6538c582c7cSDexuan Cui mtx_unlock(&hbus->device_list_lock);
6548c582c7cSDexuan Cui
6558c582c7cSDexuan Cui TAILQ_FOREACH_SAFE(hid, &hpdev->irq_desc_list, link, tmp_hid)
6568c582c7cSDexuan Cui hv_int_desc_free(hpdev, hid);
6578c582c7cSDexuan Cui
6588c582c7cSDexuan Cui free(hpdev, M_DEVBUF);
6598c582c7cSDexuan Cui }
6608c582c7cSDexuan Cui
6618c582c7cSDexuan Cui static struct hv_pci_dev *
new_pcichild_device(struct hv_pcibus * hbus,struct hv_pcidev_desc * desc)662ea11861eSWei Hu new_pcichild_device(struct hv_pcibus *hbus, struct hv_pcidev_desc *desc)
6638c582c7cSDexuan Cui {
6648c582c7cSDexuan Cui struct hv_pci_dev *hpdev;
6658c582c7cSDexuan Cui struct pci_child_message *res_req;
6668c582c7cSDexuan Cui struct q_res_req_compl comp_pkt;
6678c582c7cSDexuan Cui struct {
6688c582c7cSDexuan Cui struct pci_packet pkt;
6698c582c7cSDexuan Cui uint8_t buffer[sizeof(struct pci_child_message)];
6708c582c7cSDexuan Cui } ctxt;
6718c582c7cSDexuan Cui int ret;
6728c582c7cSDexuan Cui
6738c582c7cSDexuan Cui hpdev = malloc(sizeof(*hpdev), M_DEVBUF, M_WAITOK | M_ZERO);
6748c582c7cSDexuan Cui hpdev->hbus = hbus;
6758c582c7cSDexuan Cui
6768c582c7cSDexuan Cui TAILQ_INIT(&hpdev->irq_desc_list);
6778c582c7cSDexuan Cui
6788c582c7cSDexuan Cui init_completion(&comp_pkt.host_event);
6798c582c7cSDexuan Cui comp_pkt.hpdev = hpdev;
6808c582c7cSDexuan Cui
6818c582c7cSDexuan Cui ctxt.pkt.compl_ctxt = &comp_pkt;
6828c582c7cSDexuan Cui ctxt.pkt.completion_func = q_resource_requirements;
6838c582c7cSDexuan Cui
6848c582c7cSDexuan Cui res_req = (struct pci_child_message *)&ctxt.pkt.message;
6858c582c7cSDexuan Cui res_req->message_type.type = PCI_QUERY_RESOURCE_REQUIREMENTS;
6868c582c7cSDexuan Cui res_req->wslot.val = desc->wslot.val;
6878c582c7cSDexuan Cui
6888c582c7cSDexuan Cui ret = vmbus_chan_send(hbus->sc->chan,
6898c582c7cSDexuan Cui VMBUS_CHANPKT_TYPE_INBAND, VMBUS_CHANPKT_FLAG_RC,
690aaf13123SDimitry Andric res_req, sizeof(*res_req), (uint64_t)(uintptr_t)&ctxt.pkt);
6918c582c7cSDexuan Cui if (ret)
6928c582c7cSDexuan Cui goto err;
6938c582c7cSDexuan Cui
69475c2786cSWei Hu if (wait_for_response(hbus, &comp_pkt.host_event))
69575c2786cSWei Hu goto err;
69675c2786cSWei Hu
6978c582c7cSDexuan Cui free_completion(&comp_pkt.host_event);
6988c582c7cSDexuan Cui
6998c582c7cSDexuan Cui hpdev->desc = *desc;
7008c582c7cSDexuan Cui
7018c582c7cSDexuan Cui mtx_lock(&hbus->device_list_lock);
7026944b2e6SDexuan Cui if (TAILQ_EMPTY(&hbus->children))
7036944b2e6SDexuan Cui hbus->pci_domain = desc->ser & 0xFFFF;
7048c582c7cSDexuan Cui TAILQ_INSERT_TAIL(&hbus->children, hpdev, link);
7058c582c7cSDexuan Cui mtx_unlock(&hbus->device_list_lock);
7068c582c7cSDexuan Cui return (hpdev);
7078c582c7cSDexuan Cui err:
7088c582c7cSDexuan Cui free_completion(&comp_pkt.host_event);
7098c582c7cSDexuan Cui free(hpdev, M_DEVBUF);
7108c582c7cSDexuan Cui return (NULL);
7118c582c7cSDexuan Cui }
7128c582c7cSDexuan Cui
7138c582c7cSDexuan Cui static int
pci_rescan(device_t dev)7148c582c7cSDexuan Cui pci_rescan(device_t dev)
7158c582c7cSDexuan Cui {
7168c582c7cSDexuan Cui return (BUS_RESCAN(dev));
7178c582c7cSDexuan Cui }
7188c582c7cSDexuan Cui
7198c582c7cSDexuan Cui static void
pci_devices_present_work(void * arg,int pending __unused)7208c582c7cSDexuan Cui pci_devices_present_work(void *arg, int pending __unused)
7218c582c7cSDexuan Cui {
7228c582c7cSDexuan Cui struct hv_dr_work *dr_wrk = arg;
7238c582c7cSDexuan Cui struct hv_dr_state *dr = NULL;
7248c582c7cSDexuan Cui struct hv_pcibus *hbus;
7258c582c7cSDexuan Cui uint32_t child_no;
7268c582c7cSDexuan Cui bool found;
727ea11861eSWei Hu struct hv_pcidev_desc *new_desc;
7288c582c7cSDexuan Cui struct hv_pci_dev *hpdev, *tmp_hpdev;
7298c582c7cSDexuan Cui struct completion *query_comp;
7308c582c7cSDexuan Cui bool need_rescan = false;
7318c582c7cSDexuan Cui
7328c582c7cSDexuan Cui hbus = dr_wrk->bus;
7338c582c7cSDexuan Cui free(dr_wrk, M_DEVBUF);
7348c582c7cSDexuan Cui
7358c582c7cSDexuan Cui /* Pull this off the queue and process it if it was the last one. */
7368c582c7cSDexuan Cui mtx_lock(&hbus->device_list_lock);
7378c582c7cSDexuan Cui while (!TAILQ_EMPTY(&hbus->dr_list)) {
7388c582c7cSDexuan Cui dr = TAILQ_FIRST(&hbus->dr_list);
7398c582c7cSDexuan Cui TAILQ_REMOVE(&hbus->dr_list, dr, link);
7408c582c7cSDexuan Cui
7418c582c7cSDexuan Cui /* Throw this away if the list still has stuff in it. */
7428c582c7cSDexuan Cui if (!TAILQ_EMPTY(&hbus->dr_list)) {
7438c582c7cSDexuan Cui free(dr, M_DEVBUF);
7448c582c7cSDexuan Cui continue;
7458c582c7cSDexuan Cui }
7468c582c7cSDexuan Cui }
7478c582c7cSDexuan Cui mtx_unlock(&hbus->device_list_lock);
7488c582c7cSDexuan Cui
7498c582c7cSDexuan Cui if (!dr)
7508c582c7cSDexuan Cui return;
7518c582c7cSDexuan Cui
7528c582c7cSDexuan Cui /* First, mark all existing children as reported missing. */
7538c582c7cSDexuan Cui mtx_lock(&hbus->device_list_lock);
7548c582c7cSDexuan Cui TAILQ_FOREACH(hpdev, &hbus->children, link)
7558c582c7cSDexuan Cui hpdev->reported_missing = true;
7568c582c7cSDexuan Cui mtx_unlock(&hbus->device_list_lock);
7578c582c7cSDexuan Cui
7588c582c7cSDexuan Cui /* Next, add back any reported devices. */
7598c582c7cSDexuan Cui for (child_no = 0; child_no < dr->device_count; child_no++) {
7608c582c7cSDexuan Cui found = false;
7618c582c7cSDexuan Cui new_desc = &dr->func[child_no];
7628c582c7cSDexuan Cui
7638c582c7cSDexuan Cui mtx_lock(&hbus->device_list_lock);
7648c582c7cSDexuan Cui TAILQ_FOREACH(hpdev, &hbus->children, link) {
7658c582c7cSDexuan Cui if ((hpdev->desc.wslot.val ==
7668c582c7cSDexuan Cui new_desc->wslot.val) &&
7678c582c7cSDexuan Cui (hpdev->desc.v_id == new_desc->v_id) &&
7688c582c7cSDexuan Cui (hpdev->desc.d_id == new_desc->d_id) &&
7698c582c7cSDexuan Cui (hpdev->desc.ser == new_desc->ser)) {
7708c582c7cSDexuan Cui hpdev->reported_missing = false;
7718c582c7cSDexuan Cui found = true;
7728c582c7cSDexuan Cui break;
7738c582c7cSDexuan Cui }
7748c582c7cSDexuan Cui }
7758c582c7cSDexuan Cui mtx_unlock(&hbus->device_list_lock);
7768c582c7cSDexuan Cui
7778c582c7cSDexuan Cui if (!found) {
7788c582c7cSDexuan Cui if (!need_rescan)
7798c582c7cSDexuan Cui need_rescan = true;
7808c582c7cSDexuan Cui
7818c582c7cSDexuan Cui hpdev = new_pcichild_device(hbus, new_desc);
7828c582c7cSDexuan Cui if (!hpdev)
7838c582c7cSDexuan Cui printf("vmbus_pcib: failed to add a child\n");
7848c582c7cSDexuan Cui }
7858c582c7cSDexuan Cui }
7868c582c7cSDexuan Cui
7878c582c7cSDexuan Cui /* Remove missing device(s), if any */
7888c582c7cSDexuan Cui TAILQ_FOREACH_SAFE(hpdev, &hbus->children, link, tmp_hpdev) {
7898c582c7cSDexuan Cui if (hpdev->reported_missing)
7908c582c7cSDexuan Cui hv_pci_delete_device(hpdev);
7918c582c7cSDexuan Cui }
7928c582c7cSDexuan Cui
7938c582c7cSDexuan Cui /* Rescan the bus to find any new device, if necessary. */
7948c582c7cSDexuan Cui if (hbus->state == hv_pcibus_installed && need_rescan)
7958c582c7cSDexuan Cui pci_rescan(hbus->pci_bus);
7968c582c7cSDexuan Cui
7978c582c7cSDexuan Cui /* Wake up hv_pci_query_relations(), if it's waiting. */
7988c582c7cSDexuan Cui query_comp = hbus->query_comp;
7998c582c7cSDexuan Cui if (query_comp) {
8008c582c7cSDexuan Cui hbus->query_comp = NULL;
8018c582c7cSDexuan Cui complete(query_comp);
8028c582c7cSDexuan Cui }
8038c582c7cSDexuan Cui
8048c582c7cSDexuan Cui free(dr, M_DEVBUF);
8058c582c7cSDexuan Cui }
8068c582c7cSDexuan Cui
8078c582c7cSDexuan Cui static struct hv_pci_dev *
get_pcichild_wslot(struct hv_pcibus * hbus,uint32_t wslot)8088c582c7cSDexuan Cui get_pcichild_wslot(struct hv_pcibus *hbus, uint32_t wslot)
8098c582c7cSDexuan Cui {
8108c582c7cSDexuan Cui struct hv_pci_dev *hpdev, *ret = NULL;
8118c582c7cSDexuan Cui
8128c582c7cSDexuan Cui mtx_lock(&hbus->device_list_lock);
8138c582c7cSDexuan Cui TAILQ_FOREACH(hpdev, &hbus->children, link) {
8148c582c7cSDexuan Cui if (hpdev->desc.wslot.val == wslot) {
8158c582c7cSDexuan Cui ret = hpdev;
8168c582c7cSDexuan Cui break;
8178c582c7cSDexuan Cui }
8188c582c7cSDexuan Cui }
8198c582c7cSDexuan Cui mtx_unlock(&hbus->device_list_lock);
8208c582c7cSDexuan Cui
8218c582c7cSDexuan Cui return (ret);
8228c582c7cSDexuan Cui }
8238c582c7cSDexuan Cui
8248c582c7cSDexuan Cui static void
hv_pci_devices_present(struct hv_pcibus * hbus,struct pci_bus_relations * relations)8258c582c7cSDexuan Cui hv_pci_devices_present(struct hv_pcibus *hbus,
8268c582c7cSDexuan Cui struct pci_bus_relations *relations)
8278c582c7cSDexuan Cui {
8288c582c7cSDexuan Cui struct hv_dr_state *dr;
8298c582c7cSDexuan Cui struct hv_dr_work *dr_wrk;
8308c582c7cSDexuan Cui unsigned long dr_size;
8318c582c7cSDexuan Cui
8328c582c7cSDexuan Cui if (hbus->detaching && relations->device_count > 0)
8338c582c7cSDexuan Cui return;
8348c582c7cSDexuan Cui
8358c582c7cSDexuan Cui dr_size = offsetof(struct hv_dr_state, func) +
8368c582c7cSDexuan Cui (sizeof(struct pci_func_desc) * relations->device_count);
8378c582c7cSDexuan Cui dr = malloc(dr_size, M_DEVBUF, M_WAITOK | M_ZERO);
8388c582c7cSDexuan Cui
8398c582c7cSDexuan Cui dr->device_count = relations->device_count;
8408c582c7cSDexuan Cui if (dr->device_count != 0)
8418c582c7cSDexuan Cui memcpy(dr->func, relations->func,
842ea11861eSWei Hu sizeof(struct hv_pcidev_desc) * dr->device_count);
843ea11861eSWei Hu
844ea11861eSWei Hu mtx_lock(&hbus->device_list_lock);
845ea11861eSWei Hu TAILQ_INSERT_TAIL(&hbus->dr_list, dr, link);
846ea11861eSWei Hu mtx_unlock(&hbus->device_list_lock);
847ea11861eSWei Hu
848ea11861eSWei Hu dr_wrk = malloc(sizeof(*dr_wrk), M_DEVBUF, M_WAITOK | M_ZERO);
849ea11861eSWei Hu dr_wrk->bus = hbus;
850ea11861eSWei Hu TASK_INIT(&dr_wrk->task, 0, pci_devices_present_work, dr_wrk);
851ea11861eSWei Hu taskqueue_enqueue(hbus->sc->taskq, &dr_wrk->task);
852ea11861eSWei Hu }
853ea11861eSWei Hu
854ea11861eSWei Hu static void
hv_pci_devices_present2(struct hv_pcibus * hbus,struct pci_bus_relations2 * relations)855ea11861eSWei Hu hv_pci_devices_present2(struct hv_pcibus *hbus,
856ea11861eSWei Hu struct pci_bus_relations2 *relations)
857ea11861eSWei Hu {
858ea11861eSWei Hu struct hv_dr_state *dr;
859ea11861eSWei Hu struct hv_dr_work *dr_wrk;
860ea11861eSWei Hu unsigned long dr_size;
861ea11861eSWei Hu
862ea11861eSWei Hu if (hbus->detaching && relations->device_count > 0)
863ea11861eSWei Hu return;
864ea11861eSWei Hu
865ea11861eSWei Hu dr_size = offsetof(struct hv_dr_state, func) +
866ea11861eSWei Hu (sizeof(struct pci_func_desc2) * relations->device_count);
867ea11861eSWei Hu dr = malloc(dr_size, M_DEVBUF, M_WAITOK | M_ZERO);
868ea11861eSWei Hu
869ea11861eSWei Hu dr->device_count = relations->device_count;
870ea11861eSWei Hu if (dr->device_count != 0)
871ea11861eSWei Hu memcpy(dr->func, relations->func,
872ea11861eSWei Hu sizeof(struct pci_func_desc2) * dr->device_count);
8738c582c7cSDexuan Cui
8748c582c7cSDexuan Cui mtx_lock(&hbus->device_list_lock);
8758c582c7cSDexuan Cui TAILQ_INSERT_TAIL(&hbus->dr_list, dr, link);
8768c582c7cSDexuan Cui mtx_unlock(&hbus->device_list_lock);
8778c582c7cSDexuan Cui
8788c582c7cSDexuan Cui dr_wrk = malloc(sizeof(*dr_wrk), M_DEVBUF, M_WAITOK | M_ZERO);
8798c582c7cSDexuan Cui dr_wrk->bus = hbus;
8808c582c7cSDexuan Cui TASK_INIT(&dr_wrk->task, 0, pci_devices_present_work, dr_wrk);
8818c582c7cSDexuan Cui taskqueue_enqueue(hbus->sc->taskq, &dr_wrk->task);
8828c582c7cSDexuan Cui }
8838c582c7cSDexuan Cui
8848c582c7cSDexuan Cui static void
hv_eject_device_work(void * arg,int pending __unused)8858c582c7cSDexuan Cui hv_eject_device_work(void *arg, int pending __unused)
8868c582c7cSDexuan Cui {
8878c582c7cSDexuan Cui struct hv_pci_dev *hpdev = arg;
8888c582c7cSDexuan Cui union win_slot_encoding wslot = hpdev->desc.wslot;
8898c582c7cSDexuan Cui struct hv_pcibus *hbus = hpdev->hbus;
8908c582c7cSDexuan Cui struct pci_eject_response *eject_pkt;
8918c582c7cSDexuan Cui struct {
8928c582c7cSDexuan Cui struct pci_packet pkt;
8938c582c7cSDexuan Cui uint8_t buffer[sizeof(struct pci_eject_response)];
8948c582c7cSDexuan Cui } ctxt;
8958c582c7cSDexuan Cui
8968c582c7cSDexuan Cui hv_pci_delete_device(hpdev);
8978c582c7cSDexuan Cui
8988c582c7cSDexuan Cui memset(&ctxt, 0, sizeof(ctxt));
8998c582c7cSDexuan Cui eject_pkt = (struct pci_eject_response *)&ctxt.pkt.message;
9008c582c7cSDexuan Cui eject_pkt->message_type.type = PCI_EJECTION_COMPLETE;
9018c582c7cSDexuan Cui eject_pkt->wslot.val = wslot.val;
9028c582c7cSDexuan Cui vmbus_chan_send(hbus->sc->chan, VMBUS_CHANPKT_TYPE_INBAND, 0,
9038c582c7cSDexuan Cui eject_pkt, sizeof(*eject_pkt), 0);
9048c582c7cSDexuan Cui }
9058c582c7cSDexuan Cui
9068c582c7cSDexuan Cui static void
hv_pci_eject_device(struct hv_pci_dev * hpdev)9078c582c7cSDexuan Cui hv_pci_eject_device(struct hv_pci_dev *hpdev)
9088c582c7cSDexuan Cui {
9098c582c7cSDexuan Cui struct hv_pcibus *hbus = hpdev->hbus;
9108c582c7cSDexuan Cui struct taskqueue *taskq;
9118c582c7cSDexuan Cui
9128c582c7cSDexuan Cui if (hbus->detaching)
9138c582c7cSDexuan Cui return;
9148c582c7cSDexuan Cui
9158c582c7cSDexuan Cui /*
9168c582c7cSDexuan Cui * Push this task into the same taskqueue on which
9178c582c7cSDexuan Cui * vmbus_pcib_attach() runs, so we're sure this task can't run
9188c582c7cSDexuan Cui * concurrently with vmbus_pcib_attach().
9198c582c7cSDexuan Cui */
9208c582c7cSDexuan Cui TASK_INIT(&hpdev->eject_task, 0, hv_eject_device_work, hpdev);
9218c582c7cSDexuan Cui taskq = vmbus_chan_mgmt_tq(hbus->sc->chan);
9228c582c7cSDexuan Cui taskqueue_enqueue(taskq, &hpdev->eject_task);
9238c582c7cSDexuan Cui }
9248c582c7cSDexuan Cui
9258c582c7cSDexuan Cui #define PCIB_PACKET_SIZE 0x100
9268c582c7cSDexuan Cui
9278c582c7cSDexuan Cui static void
vmbus_pcib_on_channel_callback(struct vmbus_channel * chan,void * arg)9288c582c7cSDexuan Cui vmbus_pcib_on_channel_callback(struct vmbus_channel *chan, void *arg)
9298c582c7cSDexuan Cui {
9308c582c7cSDexuan Cui struct vmbus_pcib_softc *sc = arg;
9318c582c7cSDexuan Cui struct hv_pcibus *hbus = sc->hbus;
9328c582c7cSDexuan Cui
9338c582c7cSDexuan Cui void *buffer;
9348c582c7cSDexuan Cui int bufferlen = PCIB_PACKET_SIZE;
9358c582c7cSDexuan Cui
9368c582c7cSDexuan Cui struct pci_packet *comp_packet;
9378c582c7cSDexuan Cui struct pci_response *response;
9388c582c7cSDexuan Cui struct pci_incoming_message *new_msg;
9398c582c7cSDexuan Cui struct pci_bus_relations *bus_rel;
940ea11861eSWei Hu struct pci_bus_relations2 *bus_rel2;
9418c582c7cSDexuan Cui struct pci_dev_incoming *dev_msg;
9428c582c7cSDexuan Cui struct hv_pci_dev *hpdev;
9438c582c7cSDexuan Cui
9448c582c7cSDexuan Cui buffer = sc->rx_buf;
9458c582c7cSDexuan Cui do {
9468c582c7cSDexuan Cui struct vmbus_chanpkt_hdr *pkt = buffer;
9478c582c7cSDexuan Cui uint32_t bytes_rxed;
9488c582c7cSDexuan Cui int ret;
9498c582c7cSDexuan Cui
9508c582c7cSDexuan Cui bytes_rxed = bufferlen;
9518c582c7cSDexuan Cui ret = vmbus_chan_recv_pkt(chan, pkt, &bytes_rxed);
9528c582c7cSDexuan Cui
9538c582c7cSDexuan Cui if (ret == ENOBUFS) {
9548c582c7cSDexuan Cui /* Handle large packet */
9558c582c7cSDexuan Cui if (bufferlen > PCIB_PACKET_SIZE) {
9568c582c7cSDexuan Cui free(buffer, M_DEVBUF);
9578c582c7cSDexuan Cui buffer = NULL;
9588c582c7cSDexuan Cui }
9598c582c7cSDexuan Cui
9608c582c7cSDexuan Cui /* alloc new buffer */
961ea11861eSWei Hu buffer =
962ea11861eSWei Hu malloc(bytes_rxed, M_DEVBUF, M_WAITOK | M_ZERO);
9638c582c7cSDexuan Cui bufferlen = bytes_rxed;
9648c582c7cSDexuan Cui
9658c582c7cSDexuan Cui continue;
9668c582c7cSDexuan Cui }
9678c582c7cSDexuan Cui
9688c582c7cSDexuan Cui if (ret != 0) {
9698c582c7cSDexuan Cui /* ignore EIO or EAGAIN */
9708c582c7cSDexuan Cui break;
9718c582c7cSDexuan Cui }
9728c582c7cSDexuan Cui
9738c582c7cSDexuan Cui if (bytes_rxed <= sizeof(struct pci_response))
9748c582c7cSDexuan Cui continue;
9758c582c7cSDexuan Cui
9768c582c7cSDexuan Cui switch (pkt->cph_type) {
9778c582c7cSDexuan Cui case VMBUS_CHANPKT_TYPE_COMP:
978aaf13123SDimitry Andric comp_packet =
979aaf13123SDimitry Andric (struct pci_packet *)(uintptr_t)pkt->cph_xactid;
9808c582c7cSDexuan Cui response = (struct pci_response *)pkt;
9818c582c7cSDexuan Cui comp_packet->completion_func(comp_packet->compl_ctxt,
9828c582c7cSDexuan Cui response, bytes_rxed);
9838c582c7cSDexuan Cui break;
9848c582c7cSDexuan Cui case VMBUS_CHANPKT_TYPE_INBAND:
9858c582c7cSDexuan Cui new_msg = (struct pci_incoming_message *)buffer;
9868c582c7cSDexuan Cui
9878c582c7cSDexuan Cui switch (new_msg->message_type.type) {
9888c582c7cSDexuan Cui case PCI_BUS_RELATIONS:
9898c582c7cSDexuan Cui bus_rel = (struct pci_bus_relations *)buffer;
9908c582c7cSDexuan Cui
9918c582c7cSDexuan Cui if (bus_rel->device_count == 0)
9928c582c7cSDexuan Cui break;
9938c582c7cSDexuan Cui
9948c582c7cSDexuan Cui if (bytes_rxed <
9958c582c7cSDexuan Cui offsetof(struct pci_bus_relations, func) +
9968c582c7cSDexuan Cui (sizeof(struct pci_func_desc) *
9978c582c7cSDexuan Cui (bus_rel->device_count)))
9988c582c7cSDexuan Cui break;
9998c582c7cSDexuan Cui
10008c582c7cSDexuan Cui hv_pci_devices_present(hbus, bus_rel);
10018c582c7cSDexuan Cui break;
10028c582c7cSDexuan Cui
1003ea11861eSWei Hu case PCI_BUS_RELATIONS2:
1004ea11861eSWei Hu bus_rel2 = (struct pci_bus_relations2 *)buffer;
1005ea11861eSWei Hu
1006ea11861eSWei Hu if (bus_rel2->device_count == 0)
1007ea11861eSWei Hu break;
1008ea11861eSWei Hu
1009ea11861eSWei Hu if (bytes_rxed <
1010ea11861eSWei Hu offsetof(struct pci_bus_relations2, func) +
1011ea11861eSWei Hu (sizeof(struct pci_func_desc2) *
1012ea11861eSWei Hu (bus_rel2->device_count)))
1013ea11861eSWei Hu break;
1014ea11861eSWei Hu
1015ea11861eSWei Hu hv_pci_devices_present2(hbus, bus_rel2);
1016ea11861eSWei Hu
10178c582c7cSDexuan Cui case PCI_EJECT:
10188c582c7cSDexuan Cui dev_msg = (struct pci_dev_incoming *)buffer;
10198c582c7cSDexuan Cui hpdev = get_pcichild_wslot(hbus,
10208c582c7cSDexuan Cui dev_msg->wslot.val);
10218c582c7cSDexuan Cui
10228c582c7cSDexuan Cui if (hpdev)
10238c582c7cSDexuan Cui hv_pci_eject_device(hpdev);
10248c582c7cSDexuan Cui
10258c582c7cSDexuan Cui break;
10268c582c7cSDexuan Cui default:
10278c582c7cSDexuan Cui printf("vmbus_pcib: Unknown msg type 0x%x\n",
10288c582c7cSDexuan Cui new_msg->message_type.type);
10298c582c7cSDexuan Cui break;
10308c582c7cSDexuan Cui }
10318c582c7cSDexuan Cui break;
10328c582c7cSDexuan Cui default:
10338c582c7cSDexuan Cui printf("vmbus_pcib: Unknown VMBus msg type %hd\n",
10348c582c7cSDexuan Cui pkt->cph_type);
10358c582c7cSDexuan Cui break;
10368c582c7cSDexuan Cui }
10378c582c7cSDexuan Cui } while (1);
10388c582c7cSDexuan Cui
10398c582c7cSDexuan Cui if (bufferlen > PCIB_PACKET_SIZE)
10408c582c7cSDexuan Cui free(buffer, M_DEVBUF);
10418c582c7cSDexuan Cui }
10428c582c7cSDexuan Cui
10438c582c7cSDexuan Cui static int
hv_pci_protocol_negotiation(struct hv_pcibus * hbus,enum pci_protocol_version_t version[],int num_version)1044ea11861eSWei Hu hv_pci_protocol_negotiation(struct hv_pcibus *hbus,
1045ea11861eSWei Hu enum pci_protocol_version_t version[],
1046ea11861eSWei Hu int num_version)
10478c582c7cSDexuan Cui {
10488c582c7cSDexuan Cui struct pci_version_request *version_req;
10498c582c7cSDexuan Cui struct hv_pci_compl comp_pkt;
10508c582c7cSDexuan Cui struct {
10518c582c7cSDexuan Cui struct pci_packet pkt;
10528c582c7cSDexuan Cui uint8_t buffer[sizeof(struct pci_version_request)];
10538c582c7cSDexuan Cui } ctxt;
10548c582c7cSDexuan Cui int ret;
1055ea11861eSWei Hu int i;
10568c582c7cSDexuan Cui
10578c582c7cSDexuan Cui init_completion(&comp_pkt.host_event);
10588c582c7cSDexuan Cui
10598c582c7cSDexuan Cui ctxt.pkt.completion_func = hv_pci_generic_compl;
10608c582c7cSDexuan Cui ctxt.pkt.compl_ctxt = &comp_pkt;
10618c582c7cSDexuan Cui version_req = (struct pci_version_request *)&ctxt.pkt.message;
10628c582c7cSDexuan Cui version_req->message_type.type = PCI_QUERY_PROTOCOL_VERSION;
10638c582c7cSDexuan Cui
1064ea11861eSWei Hu for(i=0; i< num_version; i++) {
1065ea11861eSWei Hu version_req->protocol_version = version[i];
1066ea11861eSWei Hu ret = vmbus_chan_send(hbus->sc->chan,
1067ea11861eSWei Hu VMBUS_CHANPKT_TYPE_INBAND, VMBUS_CHANPKT_FLAG_RC,
1068ea11861eSWei Hu version_req, sizeof(*version_req),
1069aaf13123SDimitry Andric (uint64_t)(uintptr_t)&ctxt.pkt);
107075c2786cSWei Hu if (!ret)
107175c2786cSWei Hu ret = wait_for_response(hbus, &comp_pkt.host_event);
10728c582c7cSDexuan Cui
107375c2786cSWei Hu if (ret) {
107475c2786cSWei Hu device_printf(hbus->pcib,
107575c2786cSWei Hu "vmbus_pcib failed to request version: %d\n",
107675c2786cSWei Hu ret);
107775c2786cSWei Hu goto out;
107875c2786cSWei Hu }
10798c582c7cSDexuan Cui
1080ea11861eSWei Hu if (comp_pkt.completion_status >= 0) {
1081ea11861eSWei Hu hbus->protocol_version = version[i];
1082ea11861eSWei Hu device_printf(hbus->pcib,
1083ea11861eSWei Hu "PCI VMBus using version 0x%x\n",
1084ea11861eSWei Hu hbus->protocol_version);
1085ea11861eSWei Hu ret = 0;
1086ea11861eSWei Hu goto out;
1087ea11861eSWei Hu }
1088ea11861eSWei Hu
1089ea11861eSWei Hu if (comp_pkt.completion_status != STATUS_REVISION_MISMATCH) {
10908c582c7cSDexuan Cui device_printf(hbus->pcib,
10918c582c7cSDexuan Cui "vmbus_pcib version negotiation failed: %x\n",
10928c582c7cSDexuan Cui comp_pkt.completion_status);
10938c582c7cSDexuan Cui ret = EPROTO;
1094ea11861eSWei Hu goto out;
10958c582c7cSDexuan Cui }
1096ea11861eSWei Hu reinit_completion(&comp_pkt.host_event);
1097ea11861eSWei Hu }
1098ea11861eSWei Hu
1099ea11861eSWei Hu device_printf(hbus->pcib,
1100ea11861eSWei Hu "PCI pass-trhpugh VSP failed to find supported version\n");
11018c582c7cSDexuan Cui out:
11028c582c7cSDexuan Cui free_completion(&comp_pkt.host_event);
11038c582c7cSDexuan Cui return (ret);
11048c582c7cSDexuan Cui }
11058c582c7cSDexuan Cui
11068c582c7cSDexuan Cui /* Ask the host to send along the list of child devices */
11078c582c7cSDexuan Cui static int
hv_pci_query_relations(struct hv_pcibus * hbus)11088c582c7cSDexuan Cui hv_pci_query_relations(struct hv_pcibus *hbus)
11098c582c7cSDexuan Cui {
11108c582c7cSDexuan Cui struct pci_message message;
11118c582c7cSDexuan Cui int ret;
11128c582c7cSDexuan Cui
11138c582c7cSDexuan Cui message.type = PCI_QUERY_BUS_RELATIONS;
11148c582c7cSDexuan Cui ret = vmbus_chan_send(hbus->sc->chan, VMBUS_CHANPKT_TYPE_INBAND, 0,
11158c582c7cSDexuan Cui &message, sizeof(message), 0);
11168c582c7cSDexuan Cui return (ret);
11178c582c7cSDexuan Cui }
11188c582c7cSDexuan Cui
11198c582c7cSDexuan Cui static int
hv_pci_enter_d0(struct hv_pcibus * hbus)11208c582c7cSDexuan Cui hv_pci_enter_d0(struct hv_pcibus *hbus)
11218c582c7cSDexuan Cui {
11228c582c7cSDexuan Cui struct pci_bus_d0_entry *d0_entry;
11238c582c7cSDexuan Cui struct hv_pci_compl comp_pkt;
11248c582c7cSDexuan Cui struct {
11258c582c7cSDexuan Cui struct pci_packet pkt;
11268c582c7cSDexuan Cui uint8_t buffer[sizeof(struct pci_bus_d0_entry)];
11278c582c7cSDexuan Cui } ctxt;
11288c582c7cSDexuan Cui int ret;
11298c582c7cSDexuan Cui
11308c582c7cSDexuan Cui /*
11318c582c7cSDexuan Cui * Tell the host that the bus is ready to use, and moved into the
11328c582c7cSDexuan Cui * powered-on state. This includes telling the host which region
11338c582c7cSDexuan Cui * of memory-mapped I/O space has been chosen for configuration space
11348c582c7cSDexuan Cui * access.
11358c582c7cSDexuan Cui */
11368c582c7cSDexuan Cui init_completion(&comp_pkt.host_event);
11378c582c7cSDexuan Cui
11388c582c7cSDexuan Cui ctxt.pkt.completion_func = hv_pci_generic_compl;
11398c582c7cSDexuan Cui ctxt.pkt.compl_ctxt = &comp_pkt;
11408c582c7cSDexuan Cui
11418c582c7cSDexuan Cui d0_entry = (struct pci_bus_d0_entry *)&ctxt.pkt.message;
11428c582c7cSDexuan Cui memset(d0_entry, 0, sizeof(*d0_entry));
11438c582c7cSDexuan Cui d0_entry->message_type.type = PCI_BUS_D0ENTRY;
11448c582c7cSDexuan Cui d0_entry->mmio_base = rman_get_start(hbus->cfg_res);
11458c582c7cSDexuan Cui
11468c582c7cSDexuan Cui ret = vmbus_chan_send(hbus->sc->chan, VMBUS_CHANPKT_TYPE_INBAND,
11478c582c7cSDexuan Cui VMBUS_CHANPKT_FLAG_RC, d0_entry, sizeof(*d0_entry),
1148aaf13123SDimitry Andric (uint64_t)(uintptr_t)&ctxt.pkt);
114975c2786cSWei Hu if (!ret)
115075c2786cSWei Hu ret = wait_for_response(hbus, &comp_pkt.host_event);
115175c2786cSWei Hu
11528c582c7cSDexuan Cui if (ret)
11538c582c7cSDexuan Cui goto out;
11548c582c7cSDexuan Cui
11558c582c7cSDexuan Cui if (comp_pkt.completion_status < 0) {
11568c582c7cSDexuan Cui device_printf(hbus->pcib, "vmbus_pcib failed to enable D0\n");
11578c582c7cSDexuan Cui ret = EPROTO;
11588c582c7cSDexuan Cui } else {
11598c582c7cSDexuan Cui ret = 0;
11608c582c7cSDexuan Cui }
11618c582c7cSDexuan Cui
11628c582c7cSDexuan Cui out:
11638c582c7cSDexuan Cui free_completion(&comp_pkt.host_event);
11648c582c7cSDexuan Cui return (ret);
11658c582c7cSDexuan Cui }
11668c582c7cSDexuan Cui
11678c582c7cSDexuan Cui /*
11688c582c7cSDexuan Cui * It looks this is only needed by Windows VM, but let's send the message too
11698c582c7cSDexuan Cui * just to make the host happy.
11708c582c7cSDexuan Cui */
11718c582c7cSDexuan Cui static int
hv_send_resources_allocated(struct hv_pcibus * hbus)11728c582c7cSDexuan Cui hv_send_resources_allocated(struct hv_pcibus *hbus)
11738c582c7cSDexuan Cui {
11748c582c7cSDexuan Cui struct pci_resources_assigned *res_assigned;
1175ea11861eSWei Hu struct pci_resources_assigned2 *res_assigned2;
11768c582c7cSDexuan Cui struct hv_pci_compl comp_pkt;
11778c582c7cSDexuan Cui struct hv_pci_dev *hpdev;
11788c582c7cSDexuan Cui struct pci_packet *pkt;
11798c582c7cSDexuan Cui uint32_t wslot;
11808c582c7cSDexuan Cui int ret = 0;
1181ea11861eSWei Hu size_t size_res;
11828c582c7cSDexuan Cui
1183ea11861eSWei Hu size_res = (hbus->protocol_version < PCI_PROTOCOL_VERSION_1_4)
1184ea11861eSWei Hu ? sizeof(*res_assigned) : sizeof(*res_assigned2);
1185ea11861eSWei Hu pkt = malloc(sizeof(*pkt) + size_res,
11868c582c7cSDexuan Cui M_DEVBUF, M_WAITOK | M_ZERO);
11878c582c7cSDexuan Cui
11888c582c7cSDexuan Cui for (wslot = 0; wslot < 256; wslot++) {
11898c582c7cSDexuan Cui hpdev = get_pcichild_wslot(hbus, wslot);
11908c582c7cSDexuan Cui if (!hpdev)
11918c582c7cSDexuan Cui continue;
11928c582c7cSDexuan Cui
11938c582c7cSDexuan Cui init_completion(&comp_pkt.host_event);
11948c582c7cSDexuan Cui
1195ea11861eSWei Hu memset(pkt, 0, sizeof(*pkt) + size_res);
11968c582c7cSDexuan Cui pkt->completion_func = hv_pci_generic_compl;
11978c582c7cSDexuan Cui pkt->compl_ctxt = &comp_pkt;
11988c582c7cSDexuan Cui
1199ea11861eSWei Hu if (hbus->protocol_version < PCI_PROTOCOL_VERSION_1_4) {
1200ea11861eSWei Hu res_assigned =
1201ea11861eSWei Hu (struct pci_resources_assigned *)&pkt->message;
1202ea11861eSWei Hu res_assigned->message_type.type =
1203ea11861eSWei Hu PCI_RESOURCES_ASSIGNED;
12048c582c7cSDexuan Cui res_assigned->wslot.val = hpdev->desc.wslot.val;
1205ea11861eSWei Hu } else {
1206ea11861eSWei Hu res_assigned2 =
1207ea11861eSWei Hu (struct pci_resources_assigned2 *)&pkt->message;
1208ea11861eSWei Hu res_assigned2->message_type.type =
1209ea11861eSWei Hu PCI_RESOURCES_ASSIGNED2;
1210ea11861eSWei Hu res_assigned2->wslot.val = hpdev->desc.wslot.val;
1211ea11861eSWei Hu }
12128c582c7cSDexuan Cui
12138c582c7cSDexuan Cui ret = vmbus_chan_send(hbus->sc->chan,
12148c582c7cSDexuan Cui VMBUS_CHANPKT_TYPE_INBAND, VMBUS_CHANPKT_FLAG_RC,
1215ea11861eSWei Hu &pkt->message, size_res,
1216aaf13123SDimitry Andric (uint64_t)(uintptr_t)pkt);
121775c2786cSWei Hu if (!ret)
121875c2786cSWei Hu ret = wait_for_response(hbus, &comp_pkt.host_event);
12198c582c7cSDexuan Cui
12208c582c7cSDexuan Cui free_completion(&comp_pkt.host_event);
12218c582c7cSDexuan Cui
122275c2786cSWei Hu if (ret)
122375c2786cSWei Hu break;
122475c2786cSWei Hu
12258c582c7cSDexuan Cui if (comp_pkt.completion_status < 0) {
12268c582c7cSDexuan Cui ret = EPROTO;
12278c582c7cSDexuan Cui device_printf(hbus->pcib,
12288c582c7cSDexuan Cui "failed to send PCI_RESOURCES_ASSIGNED\n");
12298c582c7cSDexuan Cui break;
12308c582c7cSDexuan Cui }
12318c582c7cSDexuan Cui }
12328c582c7cSDexuan Cui
12338c582c7cSDexuan Cui free(pkt, M_DEVBUF);
12348c582c7cSDexuan Cui return (ret);
12358c582c7cSDexuan Cui }
12368c582c7cSDexuan Cui
12378c582c7cSDexuan Cui static int
hv_send_resources_released(struct hv_pcibus * hbus)12388c582c7cSDexuan Cui hv_send_resources_released(struct hv_pcibus *hbus)
12398c582c7cSDexuan Cui {
12408c582c7cSDexuan Cui struct pci_child_message pkt;
12418c582c7cSDexuan Cui struct hv_pci_dev *hpdev;
12428c582c7cSDexuan Cui uint32_t wslot;
12438c582c7cSDexuan Cui int ret;
12448c582c7cSDexuan Cui
12458c582c7cSDexuan Cui for (wslot = 0; wslot < 256; wslot++) {
12468c582c7cSDexuan Cui hpdev = get_pcichild_wslot(hbus, wslot);
12478c582c7cSDexuan Cui if (!hpdev)
12488c582c7cSDexuan Cui continue;
12498c582c7cSDexuan Cui
12508c582c7cSDexuan Cui pkt.message_type.type = PCI_RESOURCES_RELEASED;
12518c582c7cSDexuan Cui pkt.wslot.val = hpdev->desc.wslot.val;
12528c582c7cSDexuan Cui
12538c582c7cSDexuan Cui ret = vmbus_chan_send(hbus->sc->chan,
12548c582c7cSDexuan Cui VMBUS_CHANPKT_TYPE_INBAND, 0, &pkt, sizeof(pkt), 0);
12558c582c7cSDexuan Cui if (ret)
12568c582c7cSDexuan Cui return (ret);
12578c582c7cSDexuan Cui }
12588c582c7cSDexuan Cui
12598c582c7cSDexuan Cui return (0);
12608c582c7cSDexuan Cui }
12618c582c7cSDexuan Cui
12628c582c7cSDexuan Cui #define hv_cfg_read(x, s) \
12638c582c7cSDexuan Cui static inline uint##x##_t hv_cfg_read_##s(struct hv_pcibus *bus, \
12648c582c7cSDexuan Cui bus_size_t offset) \
12658c582c7cSDexuan Cui { \
12668c582c7cSDexuan Cui return (bus_read_##s(bus->cfg_res, offset)); \
12678c582c7cSDexuan Cui }
12688c582c7cSDexuan Cui
12698c582c7cSDexuan Cui #define hv_cfg_write(x, s) \
12708c582c7cSDexuan Cui static inline void hv_cfg_write_##s(struct hv_pcibus *bus, \
12718c582c7cSDexuan Cui bus_size_t offset, uint##x##_t val) \
12728c582c7cSDexuan Cui { \
12738c582c7cSDexuan Cui return (bus_write_##s(bus->cfg_res, offset, val)); \
12748c582c7cSDexuan Cui }
12758c582c7cSDexuan Cui
12768c582c7cSDexuan Cui hv_cfg_read(8, 1)
12778c582c7cSDexuan Cui hv_cfg_read(16, 2)
12788c582c7cSDexuan Cui hv_cfg_read(32, 4)
12798c582c7cSDexuan Cui
12808c582c7cSDexuan Cui hv_cfg_write(8, 1)
12818c582c7cSDexuan Cui hv_cfg_write(16, 2)
12828c582c7cSDexuan Cui hv_cfg_write(32, 4)
12838c582c7cSDexuan Cui
12848c582c7cSDexuan Cui static void
_hv_pcifront_read_config(struct hv_pci_dev * hpdev,int where,int size,uint32_t * val)12858c582c7cSDexuan Cui _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where, int size,
12868c582c7cSDexuan Cui uint32_t *val)
12878c582c7cSDexuan Cui {
12888c582c7cSDexuan Cui struct hv_pcibus *hbus = hpdev->hbus;
12898c582c7cSDexuan Cui bus_size_t addr = CFG_PAGE_OFFSET + where;
12908c582c7cSDexuan Cui
12918c582c7cSDexuan Cui /*
12928c582c7cSDexuan Cui * If the attempt is to read the IDs or the ROM BAR, simulate that.
12938c582c7cSDexuan Cui */
12948c582c7cSDexuan Cui if (where + size <= PCIR_COMMAND) {
12958c582c7cSDexuan Cui memcpy(val, ((uint8_t *)&hpdev->desc.v_id) + where, size);
12968c582c7cSDexuan Cui } else if (where >= PCIR_REVID && where + size <=
12978c582c7cSDexuan Cui PCIR_CACHELNSZ) {
12988c582c7cSDexuan Cui memcpy(val, ((uint8_t *)&hpdev->desc.rev) + where -
12998c582c7cSDexuan Cui PCIR_REVID, size);
13008c582c7cSDexuan Cui } else if (where >= PCIR_SUBVEND_0 && where + size <=
13018c582c7cSDexuan Cui PCIR_BIOS) {
13028c582c7cSDexuan Cui memcpy(val, (uint8_t *)&hpdev->desc.subsystem_id + where -
13038c582c7cSDexuan Cui PCIR_SUBVEND_0, size);
13048c582c7cSDexuan Cui } else if (where >= PCIR_BIOS && where + size <=
13058c582c7cSDexuan Cui PCIR_CAP_PTR) {
13068c582c7cSDexuan Cui /* ROM BARs are unimplemented */
13078c582c7cSDexuan Cui *val = 0;
13088c582c7cSDexuan Cui } else if ((where >= PCIR_INTLINE && where + size <=
13098c582c7cSDexuan Cui PCIR_INTPIN) ||(where == PCIR_INTPIN && size == 1)) {
13108c582c7cSDexuan Cui /*
13118c582c7cSDexuan Cui * Interrupt Line and Interrupt PIN are hard-wired to zero
13128c582c7cSDexuan Cui * because this front-end only supports message-signaled
13138c582c7cSDexuan Cui * interrupts.
13148c582c7cSDexuan Cui */
13158c582c7cSDexuan Cui *val = 0;
13168c582c7cSDexuan Cui } else if (where + size <= CFG_PAGE_SIZE) {
13178c582c7cSDexuan Cui mtx_lock(&hbus->config_lock);
13188c582c7cSDexuan Cui
13198c582c7cSDexuan Cui /* Choose the function to be read. */
13208c582c7cSDexuan Cui hv_cfg_write_4(hbus, 0, hpdev->desc.wslot.val);
13218c582c7cSDexuan Cui
13228c582c7cSDexuan Cui /* Make sure the function was chosen before we start reading.*/
13238c582c7cSDexuan Cui mb();
13248c582c7cSDexuan Cui
13258c582c7cSDexuan Cui /* Read from that function's config space. */
13268c582c7cSDexuan Cui switch (size) {
13278c582c7cSDexuan Cui case 1:
13288c582c7cSDexuan Cui *((uint8_t *)val) = hv_cfg_read_1(hbus, addr);
13298c582c7cSDexuan Cui break;
13308c582c7cSDexuan Cui case 2:
13318c582c7cSDexuan Cui *((uint16_t *)val) = hv_cfg_read_2(hbus, addr);
13328c582c7cSDexuan Cui break;
13338c582c7cSDexuan Cui default:
13348c582c7cSDexuan Cui *((uint32_t *)val) = hv_cfg_read_4(hbus, addr);
13358c582c7cSDexuan Cui break;
13368c582c7cSDexuan Cui }
13378c582c7cSDexuan Cui /*
13388c582c7cSDexuan Cui * Make sure the write was done before we release the lock,
13398c582c7cSDexuan Cui * allowing consecutive reads/writes.
13408c582c7cSDexuan Cui */
13418c582c7cSDexuan Cui mb();
13428c582c7cSDexuan Cui
13438c582c7cSDexuan Cui mtx_unlock(&hbus->config_lock);
13448c582c7cSDexuan Cui } else {
13458c582c7cSDexuan Cui /* Invalid config read: it's unlikely to reach here. */
13468c582c7cSDexuan Cui memset(val, 0, size);
13478c582c7cSDexuan Cui }
13488c582c7cSDexuan Cui }
13498c582c7cSDexuan Cui
13508c582c7cSDexuan Cui static void
_hv_pcifront_write_config(struct hv_pci_dev * hpdev,int where,int size,uint32_t val)13518c582c7cSDexuan Cui _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where, int size,
13528c582c7cSDexuan Cui uint32_t val)
13538c582c7cSDexuan Cui {
13548c582c7cSDexuan Cui struct hv_pcibus *hbus = hpdev->hbus;
13558c582c7cSDexuan Cui bus_size_t addr = CFG_PAGE_OFFSET + where;
13568c582c7cSDexuan Cui
13578c582c7cSDexuan Cui /* SSIDs and ROM BARs are read-only */
13588c582c7cSDexuan Cui if (where >= PCIR_SUBVEND_0 && where + size <= PCIR_CAP_PTR)
13598c582c7cSDexuan Cui return;
13608c582c7cSDexuan Cui
13618c582c7cSDexuan Cui if (where >= PCIR_COMMAND && where + size <= CFG_PAGE_SIZE) {
13628c582c7cSDexuan Cui mtx_lock(&hbus->config_lock);
13638c582c7cSDexuan Cui
13648c582c7cSDexuan Cui /* Choose the function to be written. */
13658c582c7cSDexuan Cui hv_cfg_write_4(hbus, 0, hpdev->desc.wslot.val);
13668c582c7cSDexuan Cui
13678c582c7cSDexuan Cui /* Make sure the function was chosen before we start writing.*/
13688c582c7cSDexuan Cui wmb();
13698c582c7cSDexuan Cui
13708c582c7cSDexuan Cui /* Write to that function's config space. */
13718c582c7cSDexuan Cui switch (size) {
13728c582c7cSDexuan Cui case 1:
13738c582c7cSDexuan Cui hv_cfg_write_1(hbus, addr, (uint8_t)val);
13748c582c7cSDexuan Cui break;
13758c582c7cSDexuan Cui case 2:
13768c582c7cSDexuan Cui hv_cfg_write_2(hbus, addr, (uint16_t)val);
13778c582c7cSDexuan Cui break;
13788c582c7cSDexuan Cui default:
13798c582c7cSDexuan Cui hv_cfg_write_4(hbus, addr, (uint32_t)val);
13808c582c7cSDexuan Cui break;
13818c582c7cSDexuan Cui }
13828c582c7cSDexuan Cui
13838c582c7cSDexuan Cui /*
13848c582c7cSDexuan Cui * Make sure the write was done before we release the lock,
13858c582c7cSDexuan Cui * allowing consecutive reads/writes.
13868c582c7cSDexuan Cui */
13878c582c7cSDexuan Cui mb();
13888c582c7cSDexuan Cui
13898c582c7cSDexuan Cui mtx_unlock(&hbus->config_lock);
13908c582c7cSDexuan Cui } else {
13918c582c7cSDexuan Cui /* Invalid config write: it's unlikely to reach here. */
13928c582c7cSDexuan Cui return;
13938c582c7cSDexuan Cui }
13948c582c7cSDexuan Cui }
13958c582c7cSDexuan Cui
139675412a52SWei Hu /*
139775412a52SWei Hu * The vPCI in some Hyper-V releases do not initialize the last 4
139875412a52SWei Hu * bit of BAR registers. This could result weird problems causing PCI
139975412a52SWei Hu * code fail to configure BAR correctly.
140075412a52SWei Hu *
140175412a52SWei Hu * Just write all 1's to those BARs whose probed values are not zero.
140275412a52SWei Hu * This seems to make the Hyper-V vPCI and pci_write_bar() to cooperate
140375412a52SWei Hu * correctly.
140475412a52SWei Hu */
140575412a52SWei Hu
140675412a52SWei Hu static void
vmbus_pcib_prepopulate_bars(struct hv_pcibus * hbus)140775412a52SWei Hu vmbus_pcib_prepopulate_bars(struct hv_pcibus *hbus)
140875412a52SWei Hu {
140975412a52SWei Hu struct hv_pci_dev *hpdev;
141075412a52SWei Hu int i;
141175412a52SWei Hu
141275412a52SWei Hu mtx_lock(&hbus->device_list_lock);
141375412a52SWei Hu TAILQ_FOREACH(hpdev, &hbus->children, link) {
141475412a52SWei Hu for (i = 0; i < 6; i++) {
141575412a52SWei Hu /* Ignore empty bar */
141675412a52SWei Hu if (hpdev->probed_bar[i] == 0)
141775412a52SWei Hu continue;
141875412a52SWei Hu
141975412a52SWei Hu uint32_t bar_val = 0;
142075412a52SWei Hu
142175412a52SWei Hu _hv_pcifront_read_config(hpdev, PCIR_BAR(i),
142275412a52SWei Hu 4, &bar_val);
142375412a52SWei Hu
142475412a52SWei Hu if (hpdev->probed_bar[i] != bar_val) {
142575412a52SWei Hu if (bootverbose)
142675412a52SWei Hu printf("vmbus_pcib: initialize bar %d "
142775412a52SWei Hu "by writing all 1s\n", i);
142875412a52SWei Hu
142975412a52SWei Hu _hv_pcifront_write_config(hpdev, PCIR_BAR(i),
143075412a52SWei Hu 4, 0xffffffff);
14315473dee7SWei Hu
14325473dee7SWei Hu /* Now write the original value back */
14335473dee7SWei Hu _hv_pcifront_write_config(hpdev, PCIR_BAR(i),
14345473dee7SWei Hu 4, bar_val);
143575412a52SWei Hu }
143675412a52SWei Hu }
143775412a52SWei Hu }
143875412a52SWei Hu mtx_unlock(&hbus->device_list_lock);
143975412a52SWei Hu }
144075412a52SWei Hu
14418c582c7cSDexuan Cui static void
vmbus_pcib_set_detaching(void * arg,int pending __unused)14428c582c7cSDexuan Cui vmbus_pcib_set_detaching(void *arg, int pending __unused)
14438c582c7cSDexuan Cui {
14448c582c7cSDexuan Cui struct hv_pcibus *hbus = arg;
14458c582c7cSDexuan Cui
14468c582c7cSDexuan Cui atomic_set_int(&hbus->detaching, 1);
14478c582c7cSDexuan Cui }
14488c582c7cSDexuan Cui
14498c582c7cSDexuan Cui static void
vmbus_pcib_pre_detach(struct hv_pcibus * hbus)14508c582c7cSDexuan Cui vmbus_pcib_pre_detach(struct hv_pcibus *hbus)
14518c582c7cSDexuan Cui {
14528c582c7cSDexuan Cui struct task task;
14538c582c7cSDexuan Cui
14548c582c7cSDexuan Cui TASK_INIT(&task, 0, vmbus_pcib_set_detaching, hbus);
14558c582c7cSDexuan Cui
14568c582c7cSDexuan Cui /*
14578c582c7cSDexuan Cui * Make sure the channel callback won't push any possible new
14588c582c7cSDexuan Cui * PCI_BUS_RELATIONS and PCI_EJECT tasks to sc->taskq.
14598c582c7cSDexuan Cui */
14608c582c7cSDexuan Cui vmbus_chan_run_task(hbus->sc->chan, &task);
14618c582c7cSDexuan Cui
14628c582c7cSDexuan Cui taskqueue_drain_all(hbus->sc->taskq);
14638c582c7cSDexuan Cui }
14648c582c7cSDexuan Cui
14658c582c7cSDexuan Cui
14668c582c7cSDexuan Cui /*
14678c582c7cSDexuan Cui * Standard probe entry point.
14688c582c7cSDexuan Cui *
14698c582c7cSDexuan Cui */
14708c582c7cSDexuan Cui static int
vmbus_pcib_probe(device_t dev)14718c582c7cSDexuan Cui vmbus_pcib_probe(device_t dev)
14728c582c7cSDexuan Cui {
14738c582c7cSDexuan Cui if (VMBUS_PROBE_GUID(device_get_parent(dev), dev,
14748c582c7cSDexuan Cui &g_pass_through_dev_type) == 0) {
14758c582c7cSDexuan Cui device_set_desc(dev, "Hyper-V PCI Express Pass Through");
14768c582c7cSDexuan Cui return (BUS_PROBE_DEFAULT);
14778c582c7cSDexuan Cui }
14788c582c7cSDexuan Cui return (ENXIO);
14798c582c7cSDexuan Cui }
14808c582c7cSDexuan Cui
14818c582c7cSDexuan Cui /*
14828c582c7cSDexuan Cui * Standard attach entry point.
14838c582c7cSDexuan Cui *
14848c582c7cSDexuan Cui */
14858c582c7cSDexuan Cui static int
vmbus_pcib_attach(device_t dev)14868c582c7cSDexuan Cui vmbus_pcib_attach(device_t dev)
14878c582c7cSDexuan Cui {
14888c582c7cSDexuan Cui const int pci_ring_size = (4 * PAGE_SIZE);
14898c582c7cSDexuan Cui const struct hyperv_guid *inst_guid;
14908c582c7cSDexuan Cui struct vmbus_channel *channel;
14918c582c7cSDexuan Cui struct vmbus_pcib_softc *sc;
14928c582c7cSDexuan Cui struct hv_pcibus *hbus;
14938c582c7cSDexuan Cui int rid = 0;
14948c582c7cSDexuan Cui int ret;
14958c582c7cSDexuan Cui
14968c582c7cSDexuan Cui hbus = malloc(sizeof(*hbus), M_DEVBUF, M_WAITOK | M_ZERO);
14978c582c7cSDexuan Cui hbus->pcib = dev;
14988c582c7cSDexuan Cui
14998c582c7cSDexuan Cui channel = vmbus_get_channel(dev);
15008c582c7cSDexuan Cui inst_guid = vmbus_chan_guid_inst(channel);
15018c582c7cSDexuan Cui hbus->pci_domain = inst_guid->hv_guid[9] |
15028c582c7cSDexuan Cui (inst_guid->hv_guid[8] << 8);
15038c582c7cSDexuan Cui
15048c582c7cSDexuan Cui mtx_init(&hbus->config_lock, "hbcfg", NULL, MTX_DEF);
15058c582c7cSDexuan Cui mtx_init(&hbus->device_list_lock, "hbdl", NULL, MTX_DEF);
15068c582c7cSDexuan Cui TAILQ_INIT(&hbus->children);
15078c582c7cSDexuan Cui TAILQ_INIT(&hbus->dr_list);
15088c582c7cSDexuan Cui
15098c582c7cSDexuan Cui hbus->cfg_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
15108c582c7cSDexuan Cui 0, RM_MAX_END, PCI_CONFIG_MMIO_LENGTH,
15118c582c7cSDexuan Cui RF_ACTIVE | rman_make_alignment_flags(PAGE_SIZE));
15128c582c7cSDexuan Cui
15138c582c7cSDexuan Cui if (!hbus->cfg_res) {
15148c582c7cSDexuan Cui device_printf(dev, "failed to get resource for cfg window\n");
15158c582c7cSDexuan Cui ret = ENXIO;
15168c582c7cSDexuan Cui goto free_bus;
15178c582c7cSDexuan Cui }
15188c582c7cSDexuan Cui
15198c582c7cSDexuan Cui sc = device_get_softc(dev);
15208c582c7cSDexuan Cui sc->chan = channel;
15218c582c7cSDexuan Cui sc->rx_buf = malloc(PCIB_PACKET_SIZE, M_DEVBUF, M_WAITOK | M_ZERO);
15228c582c7cSDexuan Cui sc->hbus = hbus;
15238c582c7cSDexuan Cui
15248c582c7cSDexuan Cui /*
15258c582c7cSDexuan Cui * The taskq is used to handle PCI_BUS_RELATIONS and PCI_EJECT
15268c582c7cSDexuan Cui * messages. NB: we can't handle the messages in the channel callback
15278c582c7cSDexuan Cui * directly, because the message handlers need to send new messages
15288c582c7cSDexuan Cui * to the host and waits for the host's completion messages, which
15298c582c7cSDexuan Cui * must also be handled by the channel callback.
15308c582c7cSDexuan Cui */
15318c582c7cSDexuan Cui sc->taskq = taskqueue_create("vmbus_pcib_tq", M_WAITOK,
15328c582c7cSDexuan Cui taskqueue_thread_enqueue, &sc->taskq);
15338c582c7cSDexuan Cui taskqueue_start_threads(&sc->taskq, 1, PI_NET, "vmbus_pcib_tq");
15348c582c7cSDexuan Cui
15358c582c7cSDexuan Cui hbus->sc = sc;
15368c582c7cSDexuan Cui
15378c582c7cSDexuan Cui init_completion(&hbus->query_completion);
15388c582c7cSDexuan Cui hbus->query_comp = &hbus->query_completion;
15398c582c7cSDexuan Cui
15408c582c7cSDexuan Cui ret = vmbus_chan_open(sc->chan, pci_ring_size, pci_ring_size,
15418c582c7cSDexuan Cui NULL, 0, vmbus_pcib_on_channel_callback, sc);
15428c582c7cSDexuan Cui if (ret)
15438c582c7cSDexuan Cui goto free_res;
15448c582c7cSDexuan Cui
1545ea11861eSWei Hu ret = hv_pci_protocol_negotiation(hbus, pci_protocol_versions,
1546ea11861eSWei Hu ARRAY_SIZE(pci_protocol_versions));
15478c582c7cSDexuan Cui if (ret)
15488c582c7cSDexuan Cui goto vmbus_close;
15498c582c7cSDexuan Cui
15508c582c7cSDexuan Cui ret = hv_pci_query_relations(hbus);
155175c2786cSWei Hu if (!ret)
155275c2786cSWei Hu ret = wait_for_response(hbus, hbus->query_comp);
155375c2786cSWei Hu
15548c582c7cSDexuan Cui if (ret)
15558c582c7cSDexuan Cui goto vmbus_close;
15568c582c7cSDexuan Cui
15578c582c7cSDexuan Cui ret = hv_pci_enter_d0(hbus);
15588c582c7cSDexuan Cui if (ret)
15598c582c7cSDexuan Cui goto vmbus_close;
15608c582c7cSDexuan Cui
15618c582c7cSDexuan Cui ret = hv_send_resources_allocated(hbus);
15628c582c7cSDexuan Cui if (ret)
15638c582c7cSDexuan Cui goto vmbus_close;
15648c582c7cSDexuan Cui
156575412a52SWei Hu vmbus_pcib_prepopulate_bars(hbus);
156675412a52SWei Hu
15675b56413dSWarner Losh hbus->pci_bus = device_add_child(dev, "pci", DEVICE_UNIT_ANY);
15688c582c7cSDexuan Cui if (!hbus->pci_bus) {
15698c582c7cSDexuan Cui device_printf(dev, "failed to create pci bus\n");
15708c582c7cSDexuan Cui ret = ENXIO;
15718c582c7cSDexuan Cui goto vmbus_close;
15728c582c7cSDexuan Cui }
15738c582c7cSDexuan Cui
1574*18250ec6SJohn Baldwin bus_attach_children(dev);
15758c582c7cSDexuan Cui
15768c582c7cSDexuan Cui hbus->state = hv_pcibus_installed;
15778c582c7cSDexuan Cui
15788c582c7cSDexuan Cui return (0);
15798c582c7cSDexuan Cui
15808c582c7cSDexuan Cui vmbus_close:
15818c582c7cSDexuan Cui vmbus_pcib_pre_detach(hbus);
15828c582c7cSDexuan Cui vmbus_chan_close(sc->chan);
15838c582c7cSDexuan Cui free_res:
15848c582c7cSDexuan Cui taskqueue_free(sc->taskq);
15858c582c7cSDexuan Cui free_completion(&hbus->query_completion);
15868c582c7cSDexuan Cui free(sc->rx_buf, M_DEVBUF);
15878c582c7cSDexuan Cui bus_release_resource(dev, SYS_RES_MEMORY, 0, hbus->cfg_res);
15888c582c7cSDexuan Cui free_bus:
15898c582c7cSDexuan Cui mtx_destroy(&hbus->device_list_lock);
15908c582c7cSDexuan Cui mtx_destroy(&hbus->config_lock);
15918c582c7cSDexuan Cui free(hbus, M_DEVBUF);
15928c582c7cSDexuan Cui return (ret);
15938c582c7cSDexuan Cui }
15948c582c7cSDexuan Cui
15958c582c7cSDexuan Cui /*
15968c582c7cSDexuan Cui * Standard detach entry point
15978c582c7cSDexuan Cui */
15988c582c7cSDexuan Cui static int
vmbus_pcib_detach(device_t dev)15998c582c7cSDexuan Cui vmbus_pcib_detach(device_t dev)
16008c582c7cSDexuan Cui {
16018c582c7cSDexuan Cui struct vmbus_pcib_softc *sc = device_get_softc(dev);
16028c582c7cSDexuan Cui struct hv_pcibus *hbus = sc->hbus;
16038c582c7cSDexuan Cui struct pci_message teardown_packet;
16048c582c7cSDexuan Cui struct pci_bus_relations relations;
16058c582c7cSDexuan Cui int ret;
16068c582c7cSDexuan Cui
16078c582c7cSDexuan Cui vmbus_pcib_pre_detach(hbus);
16088c582c7cSDexuan Cui
16098c582c7cSDexuan Cui if (hbus->state == hv_pcibus_installed)
16108c582c7cSDexuan Cui bus_generic_detach(dev);
16118c582c7cSDexuan Cui
16128c582c7cSDexuan Cui /* Delete any children which might still exist. */
16138c582c7cSDexuan Cui memset(&relations, 0, sizeof(relations));
16148c582c7cSDexuan Cui hv_pci_devices_present(hbus, &relations);
16158c582c7cSDexuan Cui
16168c582c7cSDexuan Cui ret = hv_send_resources_released(hbus);
16178c582c7cSDexuan Cui if (ret)
16188c582c7cSDexuan Cui device_printf(dev, "failed to send PCI_RESOURCES_RELEASED\n");
16198c582c7cSDexuan Cui
16208c582c7cSDexuan Cui teardown_packet.type = PCI_BUS_D0EXIT;
16218c582c7cSDexuan Cui ret = vmbus_chan_send(sc->chan, VMBUS_CHANPKT_TYPE_INBAND, 0,
16228c582c7cSDexuan Cui &teardown_packet, sizeof(struct pci_message), 0);
16238c582c7cSDexuan Cui if (ret)
16248c582c7cSDexuan Cui device_printf(dev, "failed to send PCI_BUS_D0EXIT\n");
16258c582c7cSDexuan Cui
16268c582c7cSDexuan Cui taskqueue_drain_all(hbus->sc->taskq);
16278c582c7cSDexuan Cui vmbus_chan_close(sc->chan);
16288c582c7cSDexuan Cui taskqueue_free(sc->taskq);
16298c582c7cSDexuan Cui
16308c582c7cSDexuan Cui free_completion(&hbus->query_completion);
16318c582c7cSDexuan Cui free(sc->rx_buf, M_DEVBUF);
16328c582c7cSDexuan Cui bus_release_resource(dev, SYS_RES_MEMORY, 0, hbus->cfg_res);
16338c582c7cSDexuan Cui
16348c582c7cSDexuan Cui mtx_destroy(&hbus->device_list_lock);
16358c582c7cSDexuan Cui mtx_destroy(&hbus->config_lock);
16368c582c7cSDexuan Cui free(hbus, M_DEVBUF);
16378c582c7cSDexuan Cui
16388c582c7cSDexuan Cui return (0);
16398c582c7cSDexuan Cui }
16408c582c7cSDexuan Cui
16418c582c7cSDexuan Cui static int
vmbus_pcib_read_ivar(device_t dev,device_t child,int which,uintptr_t * val)16428c582c7cSDexuan Cui vmbus_pcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *val)
16438c582c7cSDexuan Cui {
16448c582c7cSDexuan Cui struct vmbus_pcib_softc *sc = device_get_softc(dev);
16458c582c7cSDexuan Cui
16468c582c7cSDexuan Cui switch (which) {
16478c582c7cSDexuan Cui case PCIB_IVAR_DOMAIN:
16488c582c7cSDexuan Cui *val = sc->hbus->pci_domain;
16498c582c7cSDexuan Cui return (0);
16508c582c7cSDexuan Cui
16518c582c7cSDexuan Cui case PCIB_IVAR_BUS:
16528c582c7cSDexuan Cui /* There is only bus 0. */
16538c582c7cSDexuan Cui *val = 0;
16548c582c7cSDexuan Cui return (0);
16558c582c7cSDexuan Cui }
16568c582c7cSDexuan Cui return (ENOENT);
16578c582c7cSDexuan Cui }
16588c582c7cSDexuan Cui
16598c582c7cSDexuan Cui static int
vmbus_pcib_write_ivar(device_t dev,device_t child,int which,uintptr_t val)16608c582c7cSDexuan Cui vmbus_pcib_write_ivar(device_t dev, device_t child, int which, uintptr_t val)
16618c582c7cSDexuan Cui {
16628c582c7cSDexuan Cui return (ENOENT);
16638c582c7cSDexuan Cui }
16648c582c7cSDexuan Cui
16658c582c7cSDexuan Cui static struct resource *
vmbus_pcib_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)16668c582c7cSDexuan Cui vmbus_pcib_alloc_resource(device_t dev, device_t child, int type, int *rid,
16678c582c7cSDexuan Cui rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
16688c582c7cSDexuan Cui {
16698c582c7cSDexuan Cui unsigned int bar_no;
16708c582c7cSDexuan Cui struct hv_pci_dev *hpdev;
16718c582c7cSDexuan Cui struct vmbus_pcib_softc *sc = device_get_softc(dev);
16728c582c7cSDexuan Cui struct resource *res;
16738c582c7cSDexuan Cui unsigned int devfn;
16748c582c7cSDexuan Cui
16758c582c7cSDexuan Cui if (type == PCI_RES_BUS)
16768c582c7cSDexuan Cui return (pci_domain_alloc_bus(sc->hbus->pci_domain, child, rid,
16778c582c7cSDexuan Cui start, end, count, flags));
16788c582c7cSDexuan Cui
16798c582c7cSDexuan Cui /* Devices with port I/O BAR are not supported. */
16808c582c7cSDexuan Cui if (type == SYS_RES_IOPORT)
16818c582c7cSDexuan Cui return (NULL);
16828c582c7cSDexuan Cui
16838c582c7cSDexuan Cui if (type == SYS_RES_MEMORY) {
16848c582c7cSDexuan Cui devfn = PCI_DEVFN(pci_get_slot(child),
16858c582c7cSDexuan Cui pci_get_function(child));
16868c582c7cSDexuan Cui hpdev = get_pcichild_wslot(sc->hbus, devfn_to_wslot(devfn));
16878c582c7cSDexuan Cui if (!hpdev)
16888c582c7cSDexuan Cui return (NULL);
16898c582c7cSDexuan Cui
16908c582c7cSDexuan Cui bar_no = PCI_RID2BAR(*rid);
16918c582c7cSDexuan Cui if (bar_no >= MAX_NUM_BARS)
16928c582c7cSDexuan Cui return (NULL);
16938c582c7cSDexuan Cui
16948c582c7cSDexuan Cui /* Make sure a 32-bit BAR gets a 32-bit address */
16958c582c7cSDexuan Cui if (!(hpdev->probed_bar[bar_no] & PCIM_BAR_MEM_64))
16968c582c7cSDexuan Cui end = ulmin(end, 0xFFFFFFFF);
16978c582c7cSDexuan Cui }
16988c582c7cSDexuan Cui
16998c582c7cSDexuan Cui res = bus_generic_alloc_resource(dev, child, type, rid,
17008c582c7cSDexuan Cui start, end, count, flags);
17018c582c7cSDexuan Cui /*
17028c582c7cSDexuan Cui * If this is a request for a specific range, assume it is
17038c582c7cSDexuan Cui * correct and pass it up to the parent.
17048c582c7cSDexuan Cui */
17058c582c7cSDexuan Cui if (res == NULL && start + count - 1 == end)
17068c582c7cSDexuan Cui res = bus_generic_alloc_resource(dev, child, type, rid,
17078c582c7cSDexuan Cui start, end, count, flags);
17084196186dSLi-Wen Hsu if (res == NULL)
17094196186dSLi-Wen Hsu device_printf(dev, "vmbus_pcib_alloc_resource failed\n");
17104196186dSLi-Wen Hsu
17118c582c7cSDexuan Cui return (res);
17128c582c7cSDexuan Cui }
17138c582c7cSDexuan Cui
17148c582c7cSDexuan Cui static int
vmbus_pcib_adjust_resource(device_t dev,device_t child,struct resource * r,rman_res_t start,rman_res_t end)1715fef01f04SJohn Baldwin vmbus_pcib_adjust_resource(device_t dev, device_t child,
17169c4effb1SJohn Baldwin struct resource *r, rman_res_t start, rman_res_t end)
17179c4effb1SJohn Baldwin {
17189c4effb1SJohn Baldwin struct vmbus_pcib_softc *sc = device_get_softc(dev);
17199c4effb1SJohn Baldwin
1720fef01f04SJohn Baldwin if (rman_get_type(r) == PCI_RES_BUS)
17219c4effb1SJohn Baldwin return (pci_domain_adjust_bus(sc->hbus->pci_domain, child, r,
17229c4effb1SJohn Baldwin start, end));
1723fef01f04SJohn Baldwin return (bus_generic_adjust_resource(dev, child, r, start, end));
17249c4effb1SJohn Baldwin }
17259c4effb1SJohn Baldwin
17269c4effb1SJohn Baldwin static int
vmbus_pcib_release_resource(device_t dev,device_t child,struct resource * r)17279dbf5b0eSJohn Baldwin vmbus_pcib_release_resource(device_t dev, device_t child, struct resource *r)
17288c582c7cSDexuan Cui {
17298c582c7cSDexuan Cui struct vmbus_pcib_softc *sc = device_get_softc(dev);
17308c582c7cSDexuan Cui
17319dbf5b0eSJohn Baldwin switch (rman_get_type(r)) {
17329dbf5b0eSJohn Baldwin case PCI_RES_BUS:
17339dbf5b0eSJohn Baldwin return (pci_domain_release_bus(sc->hbus->pci_domain, child, r));
17349dbf5b0eSJohn Baldwin case SYS_RES_IOPORT:
17358c582c7cSDexuan Cui return (EINVAL);
17369dbf5b0eSJohn Baldwin default:
17379dbf5b0eSJohn Baldwin return (bus_generic_release_resource(dev, child, r));
17389dbf5b0eSJohn Baldwin }
17398c582c7cSDexuan Cui }
17408c582c7cSDexuan Cui
17418c582c7cSDexuan Cui static int
vmbus_pcib_activate_resource(device_t dev,device_t child,struct resource * r)17422baed46eSJohn Baldwin vmbus_pcib_activate_resource(device_t dev, device_t child, struct resource *r)
17439c4effb1SJohn Baldwin {
17449c4effb1SJohn Baldwin struct vmbus_pcib_softc *sc = device_get_softc(dev);
17459c4effb1SJohn Baldwin
17462baed46eSJohn Baldwin if (rman_get_type(r) == PCI_RES_BUS)
17479c4effb1SJohn Baldwin return (pci_domain_activate_bus(sc->hbus->pci_domain, child,
17482baed46eSJohn Baldwin r));
17492baed46eSJohn Baldwin return (bus_generic_activate_resource(dev, child, r));
17509c4effb1SJohn Baldwin }
17519c4effb1SJohn Baldwin
17529c4effb1SJohn Baldwin static int
vmbus_pcib_deactivate_resource(device_t dev,device_t child,struct resource * r)17532baed46eSJohn Baldwin vmbus_pcib_deactivate_resource(device_t dev, device_t child, struct resource *r)
17549c4effb1SJohn Baldwin {
17559c4effb1SJohn Baldwin struct vmbus_pcib_softc *sc = device_get_softc(dev);
17569c4effb1SJohn Baldwin
17572baed46eSJohn Baldwin if (rman_get_type(r) == PCI_RES_BUS)
17589c4effb1SJohn Baldwin return (pci_domain_deactivate_bus(sc->hbus->pci_domain, child,
17592baed46eSJohn Baldwin r));
17602baed46eSJohn Baldwin return (bus_generic_deactivate_resource(dev, child, r));
17619c4effb1SJohn Baldwin }
17629c4effb1SJohn Baldwin
17639c4effb1SJohn Baldwin static int
vmbus_pcib_get_cpus(device_t pcib,device_t dev,enum cpu_sets op,size_t setsize,cpuset_t * cpuset)17648c582c7cSDexuan Cui vmbus_pcib_get_cpus(device_t pcib, device_t dev, enum cpu_sets op,
17658c582c7cSDexuan Cui size_t setsize, cpuset_t *cpuset)
17668c582c7cSDexuan Cui {
17678c582c7cSDexuan Cui return (bus_get_cpus(pcib, op, setsize, cpuset));
17688c582c7cSDexuan Cui }
17698c582c7cSDexuan Cui
17708c582c7cSDexuan Cui static uint32_t
vmbus_pcib_read_config(device_t dev,u_int bus,u_int slot,u_int func,u_int reg,int bytes)17718c582c7cSDexuan Cui vmbus_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func,
17728c582c7cSDexuan Cui u_int reg, int bytes)
17738c582c7cSDexuan Cui {
17748c582c7cSDexuan Cui struct vmbus_pcib_softc *sc = device_get_softc(dev);
17758c582c7cSDexuan Cui struct hv_pci_dev *hpdev;
17768c582c7cSDexuan Cui unsigned int devfn = PCI_DEVFN(slot, func);
17778c582c7cSDexuan Cui uint32_t data = 0;
17788c582c7cSDexuan Cui
17798c582c7cSDexuan Cui KASSERT(bus == 0, ("bus should be 0, but is %u", bus));
17808c582c7cSDexuan Cui
17818c582c7cSDexuan Cui hpdev = get_pcichild_wslot(sc->hbus, devfn_to_wslot(devfn));
17828c582c7cSDexuan Cui if (!hpdev)
17838c582c7cSDexuan Cui return (~0);
17848c582c7cSDexuan Cui
17858c582c7cSDexuan Cui _hv_pcifront_read_config(hpdev, reg, bytes, &data);
17868c582c7cSDexuan Cui
17878c582c7cSDexuan Cui return (data);
17888c582c7cSDexuan Cui }
17898c582c7cSDexuan Cui
17908c582c7cSDexuan Cui static void
vmbus_pcib_write_config(device_t dev,u_int bus,u_int slot,u_int func,u_int reg,uint32_t data,int bytes)17918c582c7cSDexuan Cui vmbus_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func,
17928c582c7cSDexuan Cui u_int reg, uint32_t data, int bytes)
17938c582c7cSDexuan Cui {
17948c582c7cSDexuan Cui struct vmbus_pcib_softc *sc = device_get_softc(dev);
17958c582c7cSDexuan Cui struct hv_pci_dev *hpdev;
17968c582c7cSDexuan Cui unsigned int devfn = PCI_DEVFN(slot, func);
17978c582c7cSDexuan Cui
17988c582c7cSDexuan Cui KASSERT(bus == 0, ("bus should be 0, but is %u", bus));
17998c582c7cSDexuan Cui
18008c582c7cSDexuan Cui hpdev = get_pcichild_wslot(sc->hbus, devfn_to_wslot(devfn));
18018c582c7cSDexuan Cui if (!hpdev)
18028c582c7cSDexuan Cui return;
18038c582c7cSDexuan Cui
18048c582c7cSDexuan Cui _hv_pcifront_write_config(hpdev, reg, bytes, data);
18058c582c7cSDexuan Cui }
18068c582c7cSDexuan Cui
18078c582c7cSDexuan Cui static int
vmbus_pcib_route_intr(device_t pcib,device_t dev,int pin)18088c582c7cSDexuan Cui vmbus_pcib_route_intr(device_t pcib, device_t dev, int pin)
18098c582c7cSDexuan Cui {
18108c582c7cSDexuan Cui /* We only support MSI/MSI-X and don't support INTx interrupt. */
18118c582c7cSDexuan Cui return (PCI_INVALID_IRQ);
18128c582c7cSDexuan Cui }
18138c582c7cSDexuan Cui
18148c582c7cSDexuan Cui static int
vmbus_pcib_alloc_msi(device_t pcib,device_t dev,int count,int maxcount,int * irqs)18158c582c7cSDexuan Cui vmbus_pcib_alloc_msi(device_t pcib, device_t dev, int count,
18168c582c7cSDexuan Cui int maxcount, int *irqs)
18178c582c7cSDexuan Cui {
1818ea11861eSWei Hu #if defined(__amd64__) || defined(__i386__)
18198c582c7cSDexuan Cui return (PCIB_ALLOC_MSI(device_get_parent(pcib), dev, count, maxcount,
18208c582c7cSDexuan Cui irqs));
1821ea11861eSWei Hu #endif
1822ea11861eSWei Hu #if defined(__aarch64__)
1823ea11861eSWei Hu return (intr_alloc_msi(pcib, dev, ACPI_MSI_XREF, count, maxcount,
1824ea11861eSWei Hu irqs));
1825ea11861eSWei Hu #endif
18268c582c7cSDexuan Cui }
18278c582c7cSDexuan Cui
18288c582c7cSDexuan Cui static int
vmbus_pcib_release_msi(device_t pcib,device_t dev,int count,int * irqs)18298c582c7cSDexuan Cui vmbus_pcib_release_msi(device_t pcib, device_t dev, int count, int *irqs)
18308c582c7cSDexuan Cui {
1831ea11861eSWei Hu #if defined(__amd64__) || defined(__i386__)
18328c582c7cSDexuan Cui return (PCIB_RELEASE_MSI(device_get_parent(pcib), dev, count, irqs));
1833ea11861eSWei Hu #endif
1834ea11861eSWei Hu #if defined(__aarch64__)
1835ea11861eSWei Hu return(intr_release_msi(pcib, dev, ACPI_MSI_XREF, count, irqs));
1836ea11861eSWei Hu #endif
18378c582c7cSDexuan Cui }
18388c582c7cSDexuan Cui
18398c582c7cSDexuan Cui static int
vmbus_pcib_alloc_msix(device_t pcib,device_t dev,int * irq)18408c582c7cSDexuan Cui vmbus_pcib_alloc_msix(device_t pcib, device_t dev, int *irq)
18418c582c7cSDexuan Cui {
1842ea11861eSWei Hu #if defined(__aarch64__)
1843ea11861eSWei Hu int ret;
1844ea11861eSWei Hu #if defined(INTRNG)
1845ea11861eSWei Hu ret = intr_alloc_msix(pcib, dev, ACPI_MSI_XREF, irq);
1846ea11861eSWei Hu return ret;
1847ea11861eSWei Hu #else
1848ea11861eSWei Hu return (ENXIO);
1849ea11861eSWei Hu #endif
1850ea11861eSWei Hu #else
18518c582c7cSDexuan Cui return (PCIB_ALLOC_MSIX(device_get_parent(pcib), dev, irq));
1852ea11861eSWei Hu #endif /* __aarch64__ */
18538c582c7cSDexuan Cui }
18548c582c7cSDexuan Cui
18558c582c7cSDexuan Cui static int
vmbus_pcib_release_msix(device_t pcib,device_t dev,int irq)18568c582c7cSDexuan Cui vmbus_pcib_release_msix(device_t pcib, device_t dev, int irq)
18578c582c7cSDexuan Cui {
1858ea11861eSWei Hu #if defined(__aarch64__)
1859ea11861eSWei Hu return (intr_release_msix(pcib, dev, ACPI_MSI_XREF, irq));
1860ea11861eSWei Hu #else
18618c582c7cSDexuan Cui return (PCIB_RELEASE_MSIX(device_get_parent(pcib), dev, irq));
1862ea11861eSWei Hu #endif /* __aarch64__ */
18638c582c7cSDexuan Cui }
18648c582c7cSDexuan Cui
1865ea11861eSWei Hu #if defined(__aarch64__)
1866ea11861eSWei Hu #define MSI_INTEL_ADDR_DEST 0x00000000
1867ea11861eSWei Hu #define MSI_INTEL_DATA_DELFIXED 0x0
1868ea11861eSWei Hu #endif
1869ea11861eSWei Hu #if defined(__amd64__) || defined(__i386__)
18708c582c7cSDexuan Cui #define MSI_INTEL_ADDR_DEST 0x000ff000
18718c582c7cSDexuan Cui #define MSI_INTEL_DATA_INTVEC IOART_INTVEC /* Interrupt vector. */
18728c582c7cSDexuan Cui #define MSI_INTEL_DATA_DELFIXED IOART_DELFIXED
1873ea11861eSWei Hu #endif
18748c582c7cSDexuan Cui
18758c582c7cSDexuan Cui static int
vmbus_pcib_map_msi(device_t pcib,device_t child,int irq,uint64_t * addr,uint32_t * data)18768c582c7cSDexuan Cui vmbus_pcib_map_msi(device_t pcib, device_t child, int irq,
18778c582c7cSDexuan Cui uint64_t *addr, uint32_t *data)
18788c582c7cSDexuan Cui {
18798c582c7cSDexuan Cui unsigned int devfn;
18808c582c7cSDexuan Cui struct hv_pci_dev *hpdev;
18818c582c7cSDexuan Cui
18828c582c7cSDexuan Cui uint64_t v_addr;
18838c582c7cSDexuan Cui uint32_t v_data;
18848c582c7cSDexuan Cui struct hv_irq_desc *hid, *tmp_hid;
18858c582c7cSDexuan Cui unsigned int cpu, vcpu_id;
18868c582c7cSDexuan Cui unsigned int vector;
18878c582c7cSDexuan Cui
18888c582c7cSDexuan Cui struct vmbus_pcib_softc *sc = device_get_softc(pcib);
18898c582c7cSDexuan Cui struct compose_comp_ctxt comp;
18908c582c7cSDexuan Cui struct {
18918c582c7cSDexuan Cui struct pci_packet pkt;
1892ea11861eSWei Hu union {
1893ea11861eSWei Hu struct pci_create_interrupt v1;
1894ea11861eSWei Hu struct pci_create_interrupt3 v3;
1895ea11861eSWei Hu }int_pkts;
18968c582c7cSDexuan Cui } ctxt;
18978c582c7cSDexuan Cui int ret;
1898ea11861eSWei Hu uint32_t size;
18998c582c7cSDexuan Cui
19008c582c7cSDexuan Cui devfn = PCI_DEVFN(pci_get_slot(child), pci_get_function(child));
19018c582c7cSDexuan Cui hpdev = get_pcichild_wslot(sc->hbus, devfn_to_wslot(devfn));
19028c582c7cSDexuan Cui if (!hpdev)
19038c582c7cSDexuan Cui return (ENOENT);
1904ea11861eSWei Hu #if defined(__aarch64__)
1905ea11861eSWei Hu ret = intr_map_msi(pcib, child, ACPI_MSI_XREF, irq,
1906ea11861eSWei Hu &v_addr, &v_data);
1907ea11861eSWei Hu #else
19088c582c7cSDexuan Cui ret = PCIB_MAP_MSI(device_get_parent(pcib), child, irq,
19098c582c7cSDexuan Cui &v_addr, &v_data);
1910ea11861eSWei Hu #endif
19118c582c7cSDexuan Cui if (ret)
19128c582c7cSDexuan Cui return (ret);
19138c582c7cSDexuan Cui
19148c582c7cSDexuan Cui TAILQ_FOREACH_SAFE(hid, &hpdev->irq_desc_list, link, tmp_hid) {
19158c582c7cSDexuan Cui if (hid->irq == irq) {
19168c582c7cSDexuan Cui TAILQ_REMOVE(&hpdev->irq_desc_list, hid, link);
19178c582c7cSDexuan Cui hv_int_desc_free(hpdev, hid);
19188c582c7cSDexuan Cui break;
19198c582c7cSDexuan Cui }
19208c582c7cSDexuan Cui }
19218c582c7cSDexuan Cui
1922ea11861eSWei Hu #if defined(__aarch64__)
1923ea11861eSWei Hu cpu = 0;
1924ea11861eSWei Hu vcpu_id = VMBUS_GET_VCPU_ID(device_get_parent(pcib), pcib, cpu);
1925ea11861eSWei Hu vector = v_data;
1926ea11861eSWei Hu #else
1927999174baSWei Hu cpu = apic_cpuid((v_addr & MSI_INTEL_ADDR_DEST) >> 12);
19288c582c7cSDexuan Cui vcpu_id = VMBUS_GET_VCPU_ID(device_get_parent(pcib), pcib, cpu);
19298c582c7cSDexuan Cui vector = v_data & MSI_INTEL_DATA_INTVEC;
1930ea11861eSWei Hu #endif
19318c582c7cSDexuan Cui
1932999174baSWei Hu if (hpdev->hbus->protocol_version < PCI_PROTOCOL_VERSION_1_4 &&
1933999174baSWei Hu vcpu_id > 63) {
1934999174baSWei Hu /* We only support vcpu_id < 64 before vPCI version 1.4 */
1935999174baSWei Hu device_printf(pcib,
1936999174baSWei Hu "Error: "
1937999174baSWei Hu "vcpu_id %u overflowed on PCI VMBus version 0x%x\n",
1938999174baSWei Hu vcpu_id, hpdev->hbus->protocol_version);
1939999174baSWei Hu return (ENODEV);
1940999174baSWei Hu }
1941999174baSWei Hu
19428c582c7cSDexuan Cui init_completion(&comp.comp_pkt.host_event);
19438c582c7cSDexuan Cui
19448c582c7cSDexuan Cui memset(&ctxt, 0, sizeof(ctxt));
19458c582c7cSDexuan Cui ctxt.pkt.completion_func = hv_pci_compose_compl;
19468c582c7cSDexuan Cui ctxt.pkt.compl_ctxt = ∁
1947ea11861eSWei Hu switch (hpdev->hbus->protocol_version) {
1948ea11861eSWei Hu case PCI_PROTOCOL_VERSION_1_1:
1949ea11861eSWei Hu ctxt.int_pkts.v1.message_type.type =
1950ea11861eSWei Hu PCI_CREATE_INTERRUPT_MESSAGE;
1951ea11861eSWei Hu ctxt.int_pkts.v1.wslot.val = hpdev->desc.wslot.val;
1952ea11861eSWei Hu ctxt.int_pkts.v1.int_desc.vector = vector;
1953ea11861eSWei Hu ctxt.int_pkts.v1.int_desc.vector_count = 1;
1954ea11861eSWei Hu ctxt.int_pkts.v1.int_desc.delivery_mode =
1955ea11861eSWei Hu MSI_INTEL_DATA_DELFIXED;
1956ea11861eSWei Hu ctxt.int_pkts.v1.int_desc.cpu_mask = 1ULL << vcpu_id;
1957ea11861eSWei Hu size = sizeof(ctxt.int_pkts.v1);
1958ea11861eSWei Hu break;
19598c582c7cSDexuan Cui
1960ea11861eSWei Hu case PCI_PROTOCOL_VERSION_1_4:
1961ea11861eSWei Hu ctxt.int_pkts.v3.message_type.type =
1962ea11861eSWei Hu PCI_CREATE_INTERRUPT_MESSAGE3;
1963ea11861eSWei Hu ctxt.int_pkts.v3.wslot.val = hpdev->desc.wslot.val;
1964ea11861eSWei Hu ctxt.int_pkts.v3.int_desc.vector = vector;
1965ea11861eSWei Hu ctxt.int_pkts.v3.int_desc.vector_count = 1;
1966ea11861eSWei Hu ctxt.int_pkts.v3.int_desc.reserved = 0;
1967ea11861eSWei Hu ctxt.int_pkts.v3.int_desc.delivery_mode =
1968ea11861eSWei Hu MSI_INTEL_DATA_DELFIXED;
1969ea11861eSWei Hu ctxt.int_pkts.v3.int_desc.processor_count = 1;
1970ea11861eSWei Hu ctxt.int_pkts.v3.int_desc.processor_array[0] = vcpu_id;
1971ea11861eSWei Hu size = sizeof(ctxt.int_pkts.v3);
1972ea11861eSWei Hu break;
1973ea11861eSWei Hu }
19748c582c7cSDexuan Cui ret = vmbus_chan_send(sc->chan, VMBUS_CHANPKT_TYPE_INBAND,
1975ea11861eSWei Hu VMBUS_CHANPKT_FLAG_RC, &ctxt.int_pkts, size,
1976aaf13123SDimitry Andric (uint64_t)(uintptr_t)&ctxt.pkt);
19778c582c7cSDexuan Cui if (ret) {
19788c582c7cSDexuan Cui free_completion(&comp.comp_pkt.host_event);
19798c582c7cSDexuan Cui return (ret);
19808c582c7cSDexuan Cui }
19818c582c7cSDexuan Cui
19828c582c7cSDexuan Cui wait_for_completion(&comp.comp_pkt.host_event);
19838c582c7cSDexuan Cui free_completion(&comp.comp_pkt.host_event);
19848c582c7cSDexuan Cui
1985ea11861eSWei Hu if (comp.comp_pkt.completion_status < 0) {
1986ea11861eSWei Hu device_printf(pcib,
1987ea11861eSWei Hu "vmbus_pcib_map_msi completion_status %d\n",
1988ea11861eSWei Hu comp.comp_pkt.completion_status);
19898c582c7cSDexuan Cui return (EPROTO);
1990ea11861eSWei Hu }
19918c582c7cSDexuan Cui
19928c582c7cSDexuan Cui *addr = comp.int_desc.address;
19938c582c7cSDexuan Cui *data = comp.int_desc.data;
19948c582c7cSDexuan Cui
19958c582c7cSDexuan Cui hid = malloc(sizeof(struct hv_irq_desc), M_DEVBUF, M_WAITOK | M_ZERO);
19968c582c7cSDexuan Cui hid->irq = irq;
19978c582c7cSDexuan Cui hid->desc = comp.int_desc;
19988c582c7cSDexuan Cui TAILQ_INSERT_TAIL(&hpdev->irq_desc_list, hid, link);
19998c582c7cSDexuan Cui
20008c582c7cSDexuan Cui return (0);
20018c582c7cSDexuan Cui }
20028c582c7cSDexuan Cui
20038c582c7cSDexuan Cui static device_method_t vmbus_pcib_methods[] = {
20048c582c7cSDexuan Cui /* Device interface */
20058c582c7cSDexuan Cui DEVMETHOD(device_probe, vmbus_pcib_probe),
20068c582c7cSDexuan Cui DEVMETHOD(device_attach, vmbus_pcib_attach),
20078c582c7cSDexuan Cui DEVMETHOD(device_detach, vmbus_pcib_detach),
20088c582c7cSDexuan Cui DEVMETHOD(device_shutdown, bus_generic_shutdown),
20098c582c7cSDexuan Cui DEVMETHOD(device_suspend, bus_generic_suspend),
20108c582c7cSDexuan Cui DEVMETHOD(device_resume, bus_generic_resume),
20118c582c7cSDexuan Cui
20128c582c7cSDexuan Cui /* Bus interface */
20138c582c7cSDexuan Cui DEVMETHOD(bus_read_ivar, vmbus_pcib_read_ivar),
20148c582c7cSDexuan Cui DEVMETHOD(bus_write_ivar, vmbus_pcib_write_ivar),
20158c582c7cSDexuan Cui DEVMETHOD(bus_alloc_resource, vmbus_pcib_alloc_resource),
20169c4effb1SJohn Baldwin DEVMETHOD(bus_adjust_resource, vmbus_pcib_adjust_resource),
20178c582c7cSDexuan Cui DEVMETHOD(bus_release_resource, vmbus_pcib_release_resource),
20189c4effb1SJohn Baldwin DEVMETHOD(bus_activate_resource, vmbus_pcib_activate_resource),
20199c4effb1SJohn Baldwin DEVMETHOD(bus_deactivate_resource, vmbus_pcib_deactivate_resource),
20208c582c7cSDexuan Cui DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
20218c582c7cSDexuan Cui DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
20228c582c7cSDexuan Cui DEVMETHOD(bus_get_cpus, vmbus_pcib_get_cpus),
20238c582c7cSDexuan Cui
20248c582c7cSDexuan Cui /* pcib interface */
20258c582c7cSDexuan Cui DEVMETHOD(pcib_maxslots, pcib_maxslots),
20268c582c7cSDexuan Cui DEVMETHOD(pcib_read_config, vmbus_pcib_read_config),
20278c582c7cSDexuan Cui DEVMETHOD(pcib_write_config, vmbus_pcib_write_config),
20288c582c7cSDexuan Cui DEVMETHOD(pcib_route_interrupt, vmbus_pcib_route_intr),
20298c582c7cSDexuan Cui DEVMETHOD(pcib_alloc_msi, vmbus_pcib_alloc_msi),
20308c582c7cSDexuan Cui DEVMETHOD(pcib_release_msi, vmbus_pcib_release_msi),
20318c582c7cSDexuan Cui DEVMETHOD(pcib_alloc_msix, vmbus_pcib_alloc_msix),
20328c582c7cSDexuan Cui DEVMETHOD(pcib_release_msix, vmbus_pcib_release_msix),
20338c582c7cSDexuan Cui DEVMETHOD(pcib_map_msi, vmbus_pcib_map_msi),
203428586889SWarner Losh DEVMETHOD(pcib_request_feature, pcib_request_feature_allow),
20358c582c7cSDexuan Cui
20368c582c7cSDexuan Cui DEVMETHOD_END
20378c582c7cSDexuan Cui };
20388c582c7cSDexuan Cui
20398c582c7cSDexuan Cui DEFINE_CLASS_0(pcib, vmbus_pcib_driver, vmbus_pcib_methods,
20408c582c7cSDexuan Cui sizeof(struct vmbus_pcib_softc));
2041c1cef544SJohn Baldwin DRIVER_MODULE(vmbus_pcib, vmbus, vmbus_pcib_driver, 0, 0);
20428c582c7cSDexuan Cui MODULE_DEPEND(vmbus_pcib, vmbus, 1, 1, 1);
20438c582c7cSDexuan Cui MODULE_DEPEND(vmbus_pcib, pci, 1, 1, 1);
2044