diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-21 13:24:39 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-21 13:24:39 -0800 |
commit | 9c5b80b795e9c847a7b7f5e63c6bcf07873fbcdf (patch) | |
tree | 2258006102e47fd885daf3e6777f243a4378050c /drivers | |
parent | 08179b47e1fdf288e5d59f90e5ce31513bb019c3 (diff) | |
parent | 3019270282a175defc02c8331786c73e082cd2a8 (diff) |
Merge tag 'hyperv-next-signed-20210216' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux
Pull Hyper-V updates from Wei Liu:
- VMBus hardening patches from Andrea Parri and Andres Beltran.
- Patches to make Linux boot as the root partition on Microsoft
Hypervisor from Wei Liu.
- One patch to add a new sysfs interface to support hibernation on
Hyper-V from Dexuan Cui.
- Two miscellaneous clean-up patches from Colin and Gustavo.
* tag 'hyperv-next-signed-20210216' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux: (31 commits)
Revert "Drivers: hv: vmbus: Copy packets sent by Hyper-V out of the ring buffer"
iommu/hyperv: setup an IO-APIC IRQ remapping domain for root partition
x86/hyperv: implement an MSI domain for root partition
asm-generic/hyperv: import data structures for mapping device interrupts
asm-generic/hyperv: introduce hv_device_id and auxiliary structures
asm-generic/hyperv: update hv_interrupt_entry
asm-generic/hyperv: update hv_msi_entry
x86/hyperv: implement and use hv_smp_prepare_cpus
x86/hyperv: provide a bunch of helper functions
ACPI / NUMA: add a stub function for node_to_pxm()
x86/hyperv: handling hypercall page setup for root
x86/hyperv: extract partition ID from Microsoft Hypervisor if necessary
x86/hyperv: allocate output arg pages if required
clocksource/hyperv: use MSR-based access if running as root
Drivers: hv: vmbus: skip VMBus initialization if Linux is root
x86/hyperv: detect if Linux is the root partition
asm-generic/hyperv: change HV_CPU_POWER_MANAGEMENT to HV_CPU_MANAGEMENT
hv: hyperv.h: Replace one-element array with flexible-array in struct icmsg_negotiate
hv_netvsc: Restrict configurations on isolated guests
Drivers: hv: vmbus: Enforce 'VMBus version >= 5.2' on isolated guests
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clocksource/hyperv_timer.c | 3 | ||||
-rw-r--r-- | drivers/hv/channel.c | 4 | ||||
-rw-r--r-- | drivers/hv/channel_mgmt.c | 77 | ||||
-rw-r--r-- | drivers/hv/connection.c | 7 | ||||
-rw-r--r-- | drivers/hv/hv_fcopy.c | 36 | ||||
-rw-r--r-- | drivers/hv/hv_kvp.c | 122 | ||||
-rw-r--r-- | drivers/hv/hv_snapshot.c | 89 | ||||
-rw-r--r-- | drivers/hv/hv_util.c | 222 | ||||
-rw-r--r-- | drivers/hv/vmbus_drv.c | 64 | ||||
-rw-r--r-- | drivers/iommu/hyperv-iommu.c | 177 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc.c | 18 | ||||
-rw-r--r-- | drivers/pci/controller/pci-hyperv.c | 2 |
12 files changed, 607 insertions, 214 deletions
diff --git a/drivers/clocksource/hyperv_timer.c b/drivers/clocksource/hyperv_timer.c index ba04cb381cd3..269a691bd2c4 100644 --- a/drivers/clocksource/hyperv_timer.c +++ b/drivers/clocksource/hyperv_timer.c @@ -426,6 +426,9 @@ static bool __init hv_init_tsc_clocksource(void) if (!(ms_hyperv.features & HV_MSR_REFERENCE_TSC_AVAILABLE)) return false; + if (hv_root_partition) + return false; + hv_read_reference_counter = read_hv_clock_tsc; phys_addr = virt_to_phys(hv_get_tsc_page()); diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 6fb0c76bfbf8..0bd202de7960 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -618,7 +618,7 @@ static int __vmbus_open(struct vmbus_channel *newchannel, goto error_clean_ring; /* Create and init the channel open message */ - open_info = kmalloc(sizeof(*open_info) + + open_info = kzalloc(sizeof(*open_info) + sizeof(struct vmbus_channel_open_channel), GFP_KERNEL); if (!open_info) { @@ -745,7 +745,7 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) unsigned long flags; int ret; - info = kmalloc(sizeof(*info) + + info = kzalloc(sizeof(*info) + sizeof(struct vmbus_channel_gpadl_teardown), GFP_KERNEL); if (!info) return -ENOMEM; diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 1d44bb635bb8..f0ed730e2e4e 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -31,101 +31,118 @@ const struct vmbus_device vmbus_devs[] = { { .dev_type = HV_IDE, HV_IDE_GUID, .perf_device = true, + .allowed_in_isolated = false, }, /* SCSI */ { .dev_type = HV_SCSI, HV_SCSI_GUID, .perf_device = true, + .allowed_in_isolated = true, }, /* Fibre Channel */ { .dev_type = HV_FC, HV_SYNTHFC_GUID, .perf_device = true, + .allowed_in_isolated = false, }, /* Synthetic NIC */ { .dev_type = HV_NIC, HV_NIC_GUID, .perf_device = true, + .allowed_in_isolated = true, }, /* Network Direct */ { .dev_type = HV_ND, HV_ND_GUID, .perf_device = true, + .allowed_in_isolated = false, }, /* PCIE */ { .dev_type = HV_PCIE, HV_PCIE_GUID, .perf_device = false, + .allowed_in_isolated = false, }, /* Synthetic Frame Buffer */ { .dev_type = HV_FB, HV_SYNTHVID_GUID, .perf_device = false, + .allowed_in_isolated = false, }, /* Synthetic Keyboard */ { .dev_type = HV_KBD, HV_KBD_GUID, .perf_device = false, + .allowed_in_isolated = false, }, /* Synthetic MOUSE */ { .dev_type = HV_MOUSE, HV_MOUSE_GUID, .perf_device = false, + .allowed_in_isolated = false, }, /* KVP */ { .dev_type = HV_KVP, HV_KVP_GUID, .perf_device = false, + .allowed_in_isolated = false, }, /* Time Synch */ { .dev_type = HV_TS, HV_TS_GUID, .perf_device = false, + .allowed_in_isolated = true, }, /* Heartbeat */ { .dev_type = HV_HB, HV_HEART_BEAT_GUID, .perf_device = false, + .allowed_in_isolated = true, }, /* Shutdown */ { .dev_type = HV_SHUTDOWN, HV_SHUTDOWN_GUID, .perf_device = false, + .allowed_in_isolated = true, }, /* File copy */ { .dev_type = HV_FCOPY, HV_FCOPY_GUID, .perf_device = false, + .allowed_in_isolated = false, }, /* Backup */ { .dev_type = HV_BACKUP, HV_VSS_GUID, .perf_device = false, + .allowed_in_isolated = false, }, /* Dynamic Memory */ { .dev_type = HV_DM, HV_DM_GUID, .perf_device = false, + .allowed_in_isolated = false, }, /* Unknown GUID */ { .dev_type = HV_UNKNOWN, .perf_device = false, + .allowed_in_isolated = false, }, }; @@ -190,6 +207,7 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel) * vmbus_prep_negotiate_resp() - Create default response for Negotiate message * @icmsghdrp: Pointer to msg header structure * @buf: Raw buffer channel data + * @buflen: Length of the raw buffer channel data. * @fw_version: The framework versions we can support. * @fw_vercnt: The size of @fw_version. * @srv_version: The service versions we can support. @@ -202,8 +220,8 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel) * Set up and fill in default negotiate response message. * Mainly used by Hyper-V drivers. */ -bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, - u8 *buf, const int *fw_version, int fw_vercnt, +bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf, + u32 buflen, const int *fw_version, int fw_vercnt, const int *srv_version, int srv_vercnt, int *nego_fw_version, int *nego_srv_version) { @@ -215,10 +233,14 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, bool found_match = false; struct icmsg_negotiate *negop; + /* Check that there's enough space for icframe_vercnt, icmsg_vercnt */ + if (buflen < ICMSG_HDR + offsetof(struct icmsg_negotiate, reserved)) { + pr_err_ratelimited("Invalid icmsg negotiate\n"); + return false; + } + icmsghdrp->icmsgsize = 0x10; - negop = (struct icmsg_negotiate *)&buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; + negop = (struct icmsg_negotiate *)&buf[ICMSG_HDR]; icframe_major = negop->icframe_vercnt; icframe_minor = 0; @@ -226,6 +248,15 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, icmsg_major = negop->icmsg_vercnt; icmsg_minor = 0; + /* Validate negop packet */ + if (icframe_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT || + icmsg_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT || + ICMSG_NEGOTIATE_PKT_SIZE(icframe_major, icmsg_major) > buflen) { + pr_err_ratelimited("Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n", + icframe_major, icmsg_major); + goto fw_error; + } + /* * Select the framework version number we will * support. @@ -889,6 +920,20 @@ find_primary_channel_by_offer(const struct vmbus_channel_offer_channel *offer) return channel; } +static bool vmbus_is_valid_device(const guid_t *guid) +{ + u16 i; + + if (!hv_is_isolation_supported()) + return true; + + for (i = 0; i < ARRAY_SIZE(vmbus_devs); i++) { + if (guid_equal(guid, &vmbus_devs[i].guid)) + return vmbus_devs[i].allowed_in_isolated; + } + return false; +} + /* * vmbus_onoffer - Handler for channel offers from vmbus in parent partition. * @@ -903,6 +948,13 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) trace_vmbus_onoffer(offer); + if (!vmbus_is_valid_device(&offer->offer.if_type)) { + pr_err_ratelimited("Invalid offer %d from the host supporting isolation\n", + offer->child_relid); + atomic_dec(&vmbus_connection.offer_in_progress); + return; + } + oldchannel = find_primary_channel_by_offer(offer); if (oldchannel != NULL) { @@ -1049,6 +1101,18 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) mutex_lock(&vmbus_connection.channel_mutex); channel = relid2channel(rescind->child_relid); + if (channel != NULL) { + /* + * Guarantee that no other instance of vmbus_onoffer_rescind() + * has got a reference to the channel object. Synchronize on + * &vmbus_connection.channel_mutex. + */ + if (channel->rescind_ref) { + mutex_unlock(&vmbus_connection.channel_mutex); + return; + } + channel->rescind_ref = true; + } mutex_unlock(&vmbus_connection.channel_mutex); if (channel == NULL) { @@ -1102,8 +1166,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) vmbus_device_unregister(channel->device_obj); put_device(dev); } - } - if (channel->primary_channel != NULL) { + } else if (channel->primary_channel != NULL) { /* * Sub-channel is being rescinded. Following is the channel * close sequence when initiated from the driveri (refer to diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 11170d9a2e1a..c83612cddb99 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -244,6 +244,13 @@ int vmbus_connect(void) break; } + if (hv_is_isolation_supported() && version < VERSION_WIN10_V5_2) { + pr_err("Invalid VMBus version %d.%d (expected >= %d.%d) from the host supporting isolation\n", + version >> 16, version & 0xFFFF, VERSION_WIN10_V5_2 >> 16, VERSION_WIN10_V5_2 & 0xFFFF); + ret = -EINVAL; + goto cleanup; + } + vmbus_proto_version = version; pr_info("Vmbus version:%d.%d\n", version >> 16, version & 0xFFFF); diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index 5040d7e0cd9e..59ce85e00a02 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -235,15 +235,27 @@ void hv_fcopy_onchannelcallback(void *context) if (fcopy_transaction.state > HVUTIL_READY) return; - vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, - &requestid); - if (recvlen <= 0) + if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, &requestid)) { + pr_err_ratelimited("Fcopy request received. Could not read into recv buf\n"); return; + } + + if (!recvlen) + return; + + /* Ensure recvlen is big enough to read header data */ + if (recvlen < ICMSG_HDR) { + pr_err_ratelimited("Fcopy request received. Packet length too small: %d\n", + recvlen); + return; + } icmsghdr = (struct icmsg_hdr *)&recv_buffer[ sizeof(struct vmbuspipe_hdr)]; + if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) { - if (vmbus_prep_negotiate_resp(icmsghdr, recv_buffer, + if (vmbus_prep_negotiate_resp(icmsghdr, + recv_buffer, recvlen, fw_versions, FW_VER_COUNT, fcopy_versions, FCOPY_VER_COUNT, NULL, &fcopy_srv_version)) { @@ -252,10 +264,14 @@ void hv_fcopy_onchannelcallback(void *context) fcopy_srv_version >> 16, fcopy_srv_version & 0xFFFF); } - } else { - fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; + } else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) { + /* Ensure recvlen is big enough to contain hv_fcopy_hdr */ + if (recvlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) { + pr_err_ratelimited("Invalid Fcopy hdr. Packet length too small: %u\n", + recvlen); + return; + } + fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ICMSG_HDR]; /* * Stash away this global state for completing the @@ -280,6 +296,10 @@ void hv_fcopy_onchannelcallback(void *context) schedule_delayed_work(&fcopy_timeout_work, HV_UTIL_TIMEOUT * HZ); return; + } else { + pr_err_ratelimited("Fcopy request received. Invalid msg type: %d\n", + icmsghdr->icmsgtype); + return; } icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; vmbus_sendpacket(channel, recv_buffer, recvlen, requestid, diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 754d35a25a1c..b49962d312ce 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -662,71 +662,87 @@ void hv_kvp_onchannelcallback(void *context) if (kvp_transaction.state > HVUTIL_READY) return; - vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 4, &recvlen, - &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr)]; - - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - if (vmbus_prep_negotiate_resp(icmsghdrp, - recv_buffer, fw_versions, FW_VER_COUNT, - kvp_versions, KVP_VER_COUNT, - NULL, &kvp_srv_version)) { - pr_info("KVP IC version %d.%d\n", - kvp_srv_version >> 16, - kvp_srv_version & 0xFFFF); - } - } else { - kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; + if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 4, &recvlen, &requestid)) { + pr_err_ratelimited("KVP request received. Could not read into recv buf\n"); + return; + } - /* - * Stash away this global state for completing the - * transaction; note transactions are serialized. - */ + if (!recvlen) + return; - kvp_transaction.recv_len = recvlen; - kvp_transaction.recv_req_id = requestid; - kvp_transaction.kvp_msg = kvp_msg; + /* Ensure recvlen is big enough to read header data */ + if (recvlen < ICMSG_HDR) { + pr_err_ratelimited("KVP request received. Packet length too small: %d\n", + recvlen); + return; + } - if (kvp_transaction.state < HVUTIL_READY) { - /* Userspace is not registered yet */ - kvp_respond_to_host(NULL, HV_E_FAIL); - return; - } - kvp_transaction.state = HVUTIL_HOSTMSG_RECEIVED; + icmsghdrp = (struct icmsg_hdr *)&recv_buffer[sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + if (vmbus_prep_negotiate_resp(icmsghdrp, + recv_buffer, recvlen, + fw_versions, FW_VER_COUNT, + kvp_versions, KVP_VER_COUNT, + NULL, &kvp_srv_version)) { + pr_info("KVP IC version %d.%d\n", + kvp_srv_version >> 16, + kvp_srv_version & 0xFFFF); + } + } else if (icmsghdrp->icmsgtype == ICMSGTYPE_KVPEXCHANGE) { + /* + * recvlen is not checked against sizeof(struct kvp_msg) because kvp_msg contains + * a union of structs and the msg type received is not known. Code using this + * struct should provide validation when accessing its fields. + */ + kvp_msg = (struct hv_kvp_msg *)&recv_buffer[ICMSG_HDR]; - /* - * Get the information from the - * user-mode component. - * component. This transaction will be - * completed when we get the value from - * the user-mode component. - * Set a timeout to deal with - * user-mode not responding. - */ - schedule_work(&kvp_sendkey_work); - schedule_delayed_work(&kvp_timeout_work, - HV_UTIL_TIMEOUT * HZ); + /* + * Stash away this global state for completing the + * transaction; note transactions are serialized. + */ - return; + kvp_transaction.recv_len = recvlen; + kvp_transaction.recv_req_id = requestid; + kvp_transaction.kvp_msg = kvp_msg; + if (kvp_transaction.state < HVUTIL_READY) { + /* Userspace is not registered yet */ + kvp_respond_to_host(NULL, HV_E_FAIL); + return; } + kvp_transaction.state = HVUTIL_HOSTMSG_RECEIVED; - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; + /* + * Get the information from the + * user-mode component. + * component. This transaction will be + * completed when we get the value from + * the user-mode component. + * Set a timeout to deal with + * user-mode not responding. + */ + schedule_work(&kvp_sendkey_work); + schedule_delayed_work(&kvp_timeout_work, + HV_UTIL_TIMEOUT * HZ); - vmbus_sendpacket(channel, recv_buffer, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); + return; - host_negotiatied = NEGO_FINISHED; - hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); + } else { + pr_err_ratelimited("KVP request received. Invalid msg type: %d\n", + icmsghdrp->icmsgtype); + return; } + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, recv_buffer, + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); + + host_negotiatied = NEGO_FINISHED; + hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper); } static void kvp_on_reset(void) diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index 783779e4cc1a..2267bd4c3472 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -298,49 +298,64 @@ void hv_vss_onchannelcallback(void *context) if (vss_transaction.state > HVUTIL_READY) return; - vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, - &requestid); - - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr)]; - - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - if (vmbus_prep_negotiate_resp(icmsghdrp, - recv_buffer, fw_versions, FW_VER_COUNT, - vss_versions, VSS_VER_COUNT, - NULL, &vss_srv_version)) { - - pr_info("VSS IC version %d.%d\n", - vss_srv_version >> 16, - vss_srv_version & 0xFFFF); - } - } else { - vss_msg = (struct hv_vss_msg *)&recv_buffer[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; - - /* - * Stash away this global state for completing the - * transaction; note transactions are serialized. - */ - - vss_transaction.recv_len = recvlen; - vss_transaction.recv_req_id = requestid; - vss_transaction.msg = (struct hv_vss_msg *)vss_msg; - - schedule_work(&vss_handle_request_work); + if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, &requestid)) { + pr_err_ratelimited("VSS request received. Could not read into recv buf\n"); + return; + } + + if (!recvlen) + return; + + /* Ensure recvlen is big enough to read header data */ + if (recvlen < ICMSG_HDR) { + pr_err_ratelimited("VSS request received. Packet length too small: %d\n", + recvlen); + return; + } + + icmsghdrp = (struct icmsg_hdr *)&recv_buffer[sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + if (vmbus_prep_negotiate_resp(icmsghdrp, + recv_buffer, recvlen, + fw_versions, FW_VER_COUNT, + vss_versions, VSS_VER_COUNT, + NULL, &vss_srv_version)) { + + pr_info("VSS IC version %d.%d\n", + vss_srv_version >> 16, + vss_srv_version & 0xFFFF); + } + } else if (icmsghdrp->icmsgtype == ICMSGTYPE_VSS) { + /* Ensure recvlen is big enough to contain hv_vss_msg */ + if (recvlen < ICMSG_HDR + sizeof(struct hv_vss_msg)) { + pr_err_ratelimited("Invalid VSS msg. Packet length too small: %u\n", + recvlen); return; } + vss_msg = (struct hv_vss_msg *)&recv_buffer[ICMSG_HDR]; + + /* + * Stash away this global state for completing the + * transaction; note transactions are serialized. + */ - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; + vss_transaction.recv_len = recvlen; + vss_transaction.recv_req_id = requestid; + vss_transaction.msg = (struct hv_vss_msg *)vss_msg; - vmbus_sendpacket(channel, recv_buffer, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); + schedule_work(&vss_handle_request_work); + return; + } else { + pr_err_ratelimited("VSS request received. Invalid msg type: %d\n", + icmsghdrp->icmsgtype); + return; } + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | + ICMSGHDRFLAG_RESPONSE; + vmbus_sendpacket(channel, recv_buffer, recvlen, requestid, + VM_PKT_DATA_INBAND, 0); } static void vss_on_reset(void) diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 05566ecdbe4b..e4aefeb330da 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -195,73 +195,88 @@ static void shutdown_onchannelcallback(void *context) struct icmsg_hdr *icmsghdrp; - vmbus_recvpacket(channel, shut_txf_buf, - HV_HYP_PAGE_SIZE, &recvlen, &requestid); + if (vmbus_recvpacket(channel, shut_txf_buf, HV_HYP_PAGE_SIZE, &recvlen, &requestid)) { + pr_err_ratelimited("Shutdown request received. Could not read into shut txf buf\n"); + return; + } - if (recvlen > 0) { - icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[ - sizeof(struct vmbuspipe_hdr)]; + if (!recvlen) + return; - if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - if (vmbus_prep_negotiate_resp(icmsghdrp, shut_txf_buf, - fw_versions, FW_VER_COUNT, - sd_versions, SD_VER_COUNT, - NULL, &sd_srv_version)) { - pr_info("Shutdown IC version %d.%d\n", - sd_srv_version >> 16, - sd_srv_version & 0xFFFF); - } - } else { - shutdown_msg = - (struct shutdown_msg_data *)&shut_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; + /* Ensure recvlen is big enough to read header data */ + if (recvlen < ICMSG_HDR) { + pr_err_ratelimited("Shutdown request received. Packet length too small: %d\n", + recvlen); + return; + } - /* - * shutdown_msg->flags can be 0(shut down), 2(reboot), - * or 4(hibernate). It may bitwise-OR 1, which means - * performing the request by force. Linux always tries - * to perform the request by force. - */ - switch (shutdown_msg->flags) { - case 0: - case 1: - icmsghdrp->status = HV_S_OK; - work = &shutdown_work; - pr_info("Shutdown request received -" - " graceful shutdown initiated\n"); - break; - case 2: - case 3: - icmsghdrp->status = HV_S_OK; - work = &restart_work; - pr_info("Restart request received -" - " graceful restart initiated\n"); - break; - case 4: - case 5: - pr_info("Hibernation request received\n"); - icmsghdrp->status = hibernation_supported ? - HV_S_OK : HV_E_FAIL; - if (hibernation_supported) - work = &hibernate_context.work; - break; - default: - icmsghdrp->status = HV_E_FAIL; - pr_info("Shutdown request received -" - " Invalid request\n"); - break; - } + icmsghdrp = (struct icmsg_hdr *)&shut_txf_buf[sizeof(struct vmbuspipe_hdr)]; + + if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { + if (vmbus_prep_negotiate_resp(icmsghdrp, + shut_txf_buf, recvlen, + fw_versions, FW_VER_COUNT, + sd_versions, SD_VER_COUNT, + NULL, &sd_srv_version)) { + pr_info("Shutdown IC version %d.%d\n", + sd_srv_version >> 16, + sd_srv_version & 0xFFFF); + } + } else if (icmsghdrp->icmsgtype == ICMSGTYPE_SHUTDOWN) { + /* Ensure recvlen is big enough to contain shutdown_msg_data struct */ + if (recvlen < ICMSG_HDR + sizeof(struct shutdown_msg_data)) { + pr_err_ratelimited("Invalid shutdown msg data. Packet length too small: %u\n", + recvlen); + return; } - icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION - | ICMSGHDRFLAG_RESPONSE; - - vmbus_sendpacket(channel, shut_txf_buf, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); + shutdown_msg = (struct shutdown_msg_data *)&shut_txf_buf[ICMSG_HDR]; + + /* + * shutdown_msg->flags can be 0(shut down), 2(reboot), + * or 4(hibernate). It may bitwise-OR 1, which means + * performing the request by force. Linux always tries + * to perform the request by force. + */ + switch (shutdown_msg->flags) { + case 0: + case 1: + icmsghdrp->status = HV_S_OK; + work = &shutdown_work; + pr_info("Shutdown request received - graceful shutdown initiated\n"); + break; + case 2: + case 3: + icmsghdrp->status = HV_S_OK; + work = &restart_work; + pr_info("Restart request received - graceful restart initiated\n"); + break; + case 4: + case 5: + pr_info("Hibernation request received\n"); + icmsghdrp->status = hibernation_supported ? + HV_S_OK : HV_E_FAIL; + if (hibernation_supported) + work = &hibernate_context.work; + break; + default: + icmsghdrp->status = HV_E_FAIL; + pr_info("Shutdown request received - Invalid request\n"); + break; + } + } else { + icmsghdrp->status = HV_E_FAIL; + pr_err_ratelimited("Shutdown request received. Invalid msg type: %d\n", + icmsghdrp->icmsgtype); } + icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION + | ICMSGHDRFLAG_RESPONSE; + + vmbus_sendpacket(channel, shut_txf_buf, + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); + if (work) schedule_work(work); } @@ -396,19 +411,27 @@ static void timesync_onchannelcallback(void *context) HV_HYP_PAGE_SIZE, &recvlen, &requestid); if (ret) { - pr_warn_once("TimeSync IC pkt recv failed (Err: %d)\n", - ret); + pr_err_ratelimited("TimeSync IC pkt recv failed (Err: %d)\n", + ret); break; } if (!recvlen) break; + /* Ensure recvlen is big enough to read header data */ + if (recvlen < ICMSG_HDR) { + pr_err_ratelimited("Timesync request received. Packet length too small: %d\n", + recvlen); + break; + } + icmsghdrp = (struct icmsg_hdr *)&time_txf_buf[ sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { - if (vmbus_prep_negotiate_resp(icmsghdrp, time_txf_buf, + if (vmbus_prep_negotiate_resp(icmsghdrp, + time_txf_buf, recvlen, fw_versions, FW_VER_COUNT, ts_versions, TS_VER_COUNT, NULL, &ts_srv_version)) { @@ -416,33 +439,44 @@ static void timesync_onchannelcallback(void *context) ts_srv_version >> 16, ts_srv_version & 0xFFFF); } - } else { + } else if (icmsghdrp->icmsgtype == ICMSGTYPE_TIMESYNC) { if (ts_srv_version > TS_VERSION_3) { - refdata = (struct ictimesync_ref_data *) - &time_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; + /* Ensure recvlen is big enough to read ictimesync_ref_data */ + if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_ref_data)) { + pr_err_ratelimited("Invalid ictimesync ref data. Length too small: %u\n", + recvlen); + break; + } + refdata = (struct ictimesync_ref_data *)&time_txf_buf[ICMSG_HDR]; adj_guesttime(refdata->parenttime, refdata->vmreferencetime, refdata->flags); } else { - timedatap = (struct ictimesync_data *) - &time_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; + /* Ensure recvlen is big enough to read ictimesync_data */ + if (recvlen < ICMSG_HDR + sizeof(struct ictimesync_data)) { + pr_err_ratelimited("Invalid ictimesync data. Length too small: %u\n", + recvlen); + break; + } + timedatap = (struct ictimesync_data *)&time_txf_buf[ICMSG_HDR]; + adj_guesttime(timedatap->parenttime, hv_read_reference_counter(), timedatap->flags); } + } else { + icmsghdrp->status = HV_E_FAIL; + pr_err_ratelimited("Timesync request received. Invalid msg type: %d\n", + icmsghdrp->icmsgtype); } icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; vmbus_sendpacket(channel, time_txf_buf, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); } } @@ -462,18 +496,28 @@ static void heartbeat_onchannelcallback(void *context) while (1) { - vmbus_recvpacket(channel, hbeat_txf_buf, - HV_HYP_PAGE_SIZE, &recvlen, &requestid); + if (vmbus_recvpacket(channel, hbeat_txf_buf, HV_HYP_PAGE_SIZE, + &recvlen, &requestid)) { + pr_err_ratelimited("Heartbeat request received. Could not read into hbeat txf buf\n"); + return; + } if (!recvlen) break; + /* Ensure recvlen is big enough to read header data */ + if (recvlen < ICMSG_HDR) { + pr_err_ratelimited("Heartbeat request received. Packet length too small: %d\n", + recvlen); + break; + } + icmsghdrp = (struct icmsg_hdr *)&hbeat_txf_buf[ sizeof(struct vmbuspipe_hdr)]; if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) { if (vmbus_prep_negotiate_resp(icmsghdrp, - hbeat_txf_buf, + hbeat_txf_buf, recvlen, fw_versions, FW_VER_COUNT, hb_versions, HB_VER_COUNT, NULL, &hb_srv_version)) { @@ -482,21 +526,31 @@ static void heartbeat_onchannelcallback(void *context) hb_srv_version >> 16, hb_srv_version & 0xFFFF); } - } else { - heartbeat_msg = - (struct heartbeat_msg_data *)&hbeat_txf_buf[ - sizeof(struct vmbuspipe_hdr) + - sizeof(struct icmsg_hdr)]; + } else if (icmsghdrp->icmsgtype == ICMSGTYPE_HEARTBEAT) { + /* + * Ensure recvlen is big enough to read seq_num. Reserved area is not + * included in the check as the host may not fill it up entirely + */ + if (recvlen < ICMSG_HDR + sizeof(u64)) { + pr_err_ratelimited("Invalid heartbeat msg data. Length too small: %u\n", + recvlen); + break; + } + heartbeat_msg = (struct heartbeat_msg_data *)&hbeat_txf_buf[ICMSG_HDR]; heartbeat_msg->seq_num += 1; + } else { + icmsghdrp->status = HV_E_FAIL; + pr_err_ratelimited("Heartbeat request received. Invalid msg type: %d\n", + icmsghdrp->icmsgtype); } icmsghdrp->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE; vmbus_sendpacket(channel, hbeat_txf_buf, - recvlen, requestid, - VM_PKT_DATA_INBAND, 0); + recvlen, requestid, + VM_PKT_DATA_INBAND, 0); } } diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index d491fdcee61f..10dce9f91216 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -678,6 +678,23 @@ static const struct attribute_group vmbus_dev_group = { }; __ATTRIBUTE_GROUPS(vmbus_dev); +/* Set up the attribute for /sys/bus/vmbus/hibernation */ +static ssize_t hibernation_show(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "%d\n", !!hv_is_hibernation_supported()); +} + +static BUS_ATTR_RO(hibernation); + +static struct attribute *vmbus_bus_attrs[] = { + &bus_attr_hibernation.attr, + NULL, +}; +static const struct attribute_group vmbus_bus_group = { + .attrs = vmbus_bus_attrs, +}; +__ATTRIBUTE_GROUPS(vmbus_bus); + /* * vmbus_uevent - add uevent for our device * @@ -1024,6 +1041,7 @@ static struct bus_type hv_bus = { .uevent = vmbus_uevent, .dev_groups = vmbus_dev_groups, .drv_groups = vmbus_drv_groups, + .bus_groups = vmbus_bus_groups, .pm = &vmbus_pm, }; @@ -1054,12 +1072,14 @@ void vmbus_on_msg_dpc(unsigned long data) { struct hv_per_cpu_context *hv_cpu = (void *)data; void *page_addr = hv_cpu->synic_message_page; - struct hv_message *msg = (struct hv_message *)page_addr + + struct hv_message msg_copy, *msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; struct vmbus_channel_message_header *hdr; + enum vmbus_channel_message_type msgtype; const struct vmbus_channel_message_table_entry *entry; struct onmessage_work_context *ctx; - u32 message_type = msg->header.message_type; + __u8 payload_size; + u32 message_type; /* * 'enum vmbus_channel_message_type' is supposed to always be 'u32' as @@ -1068,45 +1088,52 @@ void vmbus_on_msg_dpc(unsigned long data) */ BUILD_BUG_ON(sizeof(enum vmbus_channel_message_type) != sizeof(u32)); + /* + * Since the message is in memory shared with the host, an erroneous or + * malicious Hyper-V could modify the message while vmbus_on_msg_dpc() + * or individual message handlers are executing; to prevent this, copy + * the message into private memory. + */ + memcpy(&msg_copy, msg, sizeof(struct hv_message)); + + message_type = msg_copy.header.message_type; if (message_type == HVMSG_NONE) /* no msg */ return; - hdr = (struct vmbus_channel_message_header *)msg->u.payload; + hdr = (struct vmbus_channel_message_header *)msg_copy.u.payload; + msgtype = hdr->msgtype; trace_vmbus_on_msg_dpc(hdr); - if (hdr->msgtype >= CHANNELMSG_COUNT) { - WARN_ONCE(1, "unknown msgtype=%d\n", hdr->msgtype); + if (msgtype >= CHANNELMSG_COUNT) { + WARN_ONCE(1, "unknown msgtype=%d\n", msgtype); goto msg_handled; } - if (msg->header.payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) { - WARN_ONCE(1, "payload size is too large (%d)\n", - msg->header.payload_size); + payload_size = msg_copy.header.payload_size; + if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) { + WARN_ONCE(1, "payload size is too large (%d)\n", payload_size); goto msg_handled; } - entry = &channel_message_table[hdr->msgtype]; + entry = &channel_message_table[msgtype]; if (!entry->message_handler) goto msg_handled; - if (msg->header.payload_size < entry->min_payload_len) { - WARN_ONCE(1, "message too short: msgtype=%d len=%d\n", - hdr->msgtype, msg->header.payload_size); + if (payload_size < entry->min_payload_len) { + WARN_ONCE(1, "message too short: msgtype=%d len=%d\n", msgtype, payload_size); goto msg_handled; } if (entry->handler_type == VMHT_BLOCKING) { - ctx = kmalloc(sizeof(*ctx) + msg->header.payload_size, - GFP_ATOMIC); + ctx = kmalloc(sizeof(*ctx) + payload_size, GFP_ATOMIC); if (ctx == NULL) return; INIT_WORK(&ctx->work, vmbus_onmessage_work); - memcpy(&ctx->msg, msg, sizeof(msg->header) + - msg->header.payload_size); + memcpy(&ctx->msg, &msg_copy, sizeof(msg->header) + payload_size); /* * The host can generate a rescind message while we @@ -1115,7 +1142,7 @@ void vmbus_on_msg_dpc(unsigned long data) * by offer_in_progress and by channel_mutex. See also the * inline comments in vmbus_onoffer_rescind(). */ - switch (hdr->msgtype) { + switch (msgtype) { case CHANNELMSG_RESCIND_CHANNELOFFER: /* * If we are handling the rescind message; @@ -2618,6 +2645,9 @@ static int __init hv_acpi_init(void) if (!hv_is_hyperv_initialized()) return -ENODEV; + if (hv_root_partition) + return 0; + init_completion(&probe_event); /* diff --git a/drivers/iommu/hyperv-iommu.c b/drivers/iommu/hyperv-iommu.c index 1d21a0b5f724..e285a220c913 100644 --- a/drivers/iommu/hyperv-iommu.c +++ b/drivers/iommu/hyperv-iommu.c @@ -20,6 +20,7 @@ #include <asm/io_apic.h> #include <asm/irq_remapping.h> #include <asm/hypervisor.h> +#include <asm/mshyperv.h> #include "irq_remapping.h" @@ -115,30 +116,43 @@ static const struct irq_domain_ops hyperv_ir_domain_ops = { .free = hyperv_irq_remapping_free, }; +static const struct irq_domain_ops hyperv_root_ir_domain_ops; static int __init hyperv_prepare_irq_remapping(void) { struct fwnode_handle *fn; int i; + const char *name; + const struct irq_domain_ops *ops; if (!hypervisor_is_type(X86_HYPER_MS_HYPERV) || x86_init.hyper.msi_ext_dest_id() || !x2apic_supported()) return -ENODEV; - fn = irq_domain_alloc_named_id_fwnode("HYPERV-IR", 0); + if (hv_root_partition) { + name = "HYPERV-ROOT-IR"; + ops = &hyperv_root_ir_domain_ops; + } else { + name = "HYPERV-IR"; + ops = &hyperv_ir_domain_ops; + } + + fn = irq_domain_alloc_named_id_fwnode(name, 0); if (!fn) return -ENOMEM; ioapic_ir_domain = irq_domain_create_hierarchy(arch_get_ir_parent_domain(), - 0, IOAPIC_REMAPPING_ENTRY, fn, - &hyperv_ir_domain_ops, NULL); + 0, IOAPIC_REMAPPING_ENTRY, fn, ops, NULL); if (!ioapic_ir_domain) { irq_domain_free_fwnode(fn); return -ENOMEM; } + if (hv_root_partition) + return 0; /* The rest is only relevant to guests */ + /* * Hyper-V doesn't provide irq remapping function for * IO-APIC and so IO-APIC only accepts 8-bit APIC ID. @@ -166,4 +180,161 @@ struct irq_remap_ops hyperv_irq_remap_ops = { .enable = hyperv_enable_irq_remapping, }; +/* IRQ remapping domain when Linux runs as the root partition */ +struct hyperv_root_ir_data { + u8 ioapic_id; + bool is_level; + struct hv_interrupt_entry entry; +}; + +static void +hyperv_root_ir_compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg) +{ + u64 status; + u32 vector; + struct irq_cfg *cfg; + int ioapic_id; + struct cpumask *affinity; + int cpu; + struct hv_interrupt_entry entry; + struct hyperv_root_ir_data *data = irq_data->chip_data; + struct IO_APIC_route_entry e; + + cfg = irqd_cfg(irq_data); + affinity = irq_data_get_effective_affinity_mask(irq_data); + cpu = cpumask_first_and(affinity, cpu_online_mask); + + vector = cfg->vector; + ioapic_id = data->ioapic_id; + + if (data->entry.source == HV_DEVICE_TYPE_IOAPIC + && data->entry.ioapic_rte.as_uint64) { + entry = data->entry; + + status = hv_unmap_ioapic_interrupt(ioapic_id, &entry); + + if (status != HV_STATUS_SUCCESS) + pr_debug("%s: unexpected unmap status %lld\n", __func__, status); + + data->entry.ioapic_rte.as_uint64 = 0; + data->entry.source = 0; /* Invalid source */ + } + + + status = hv_map_ioapic_interrupt(ioapic_id, data->is_level, cpu, + vector, &entry); + + if (status != HV_STATUS_SUCCESS) { + pr_err("%s: map hypercall failed, status %lld\n", __func__, status); + return; + } + + data->entry = entry; + + /* Turn it into an IO_APIC_route_entry, and generate MSI MSG. */ + e.w1 = entry.ioapic_rte.low_uint32; + e.w2 = entry.ioapic_rte.high_uint32; + + memset(msg, 0, sizeof(*msg)); + msg->arch_data.vector = e.vector; + msg->arch_data.delivery_mode = e.delivery_mode; + msg->arch_addr_lo.dest_mode_logical = e.dest_mode_logical; + msg->arch_addr_lo.dmar_format = e.ir_format; + msg->arch_addr_lo.dmar_index_0_14 = e.ir_index_0_14; +} + +static int hyperv_root_ir_set_affinity(struct irq_data *data, + const struct cpumask *mask, bool force) +{ + struct irq_data *parent = data->parent_data; + struct irq_cfg *cfg = irqd_cfg(data); + int ret; + + ret = parent->chip->irq_set_affinity(parent, mask, force); + if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE) + return ret; + + send_cleanup_vector(cfg); + + return 0; +} + +static struct irq_chip hyperv_root_ir_chip = { + .name = "HYPERV-ROOT-IR", + .irq_ack = apic_ack_irq, + .irq_set_affinity = hyperv_root_ir_set_affinity, + .irq_compose_msi_msg = hyperv_root_ir_compose_msi_msg, +}; + +static int hyperv_root_irq_remapping_alloc(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs, + void *arg) +{ + struct irq_alloc_info *info = arg; + struct irq_data *irq_data; + struct hyperv_root_ir_data *data; + int ret = 0; + + if (!info || info->type != X86_IRQ_ALLOC_TYPE_IOAPIC || nr_irqs > 1) + return -EINVAL; + + ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg); + if (ret < 0) + return ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + irq_domain_free_irqs_common(domain, virq, nr_irqs); + return -ENOMEM; + } + + irq_data = irq_domain_get_irq_data(domain, virq); + if (!irq_data) { + kfree(data); + irq_domain_free_irqs_common(domain, virq, nr_irqs); + return -EINVAL; + } + + data->ioapic_id = info->devid; + data->is_level = info->ioapic.is_level; + + irq_data->chip = &hyperv_root_ir_chip; + irq_data->chip_data = data; + + return 0; +} + +static void hyperv_root_irq_remapping_free(struct irq_domain *domain, + unsigned int virq, unsigned int nr_irqs) +{ + struct irq_data *irq_data; + struct hyperv_root_ir_data *data; + struct hv_interrupt_entry *e; + int i; + + for (i = 0; i < nr_irqs; i++) { + irq_data = irq_domain_get_irq_data(domain, virq + i); + + if (irq_data && irq_data->chip_data) { + data = irq_data->chip_data; + e = &data->entry; + + if (e->source == HV_DEVICE_TYPE_IOAPIC + && e->ioapic_rte.as_uint64) + hv_unmap_ioapic_interrupt(data->ioapic_id, + &data->entry); + + kfree(data); + } + } + + irq_domain_free_irqs_common(domain, virq, nr_irqs); +} + +static const struct irq_domain_ops hyperv_root_ir_domain_ops = { + .select = hyperv_irq_remapping_select, + .alloc = hyperv_root_irq_remapping_alloc, + .free = hyperv_root_irq_remapping_free, +}; + #endif diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index dc3f73c3b33e..c64cc7639c39 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -22,6 +22,7 @@ #include <linux/prefetch.h> #include <asm/sync_bitops.h> +#include <asm/mshyperv.h> #include "hyperv_net.h" #include "netvsc_trace.h" @@ -562,7 +563,10 @@ static int negotiate_nvsp_ver(struct hv_device *device, init_packet->msg.v2_msg.send_ndis_config.capability.ieee8021q = 1; if (nvsp_ver >= NVSP_PROTOCOL_VERSION_5) { - init_packet->msg.v2_msg.send_ndis_config.capability.sriov = 1; + if (hv_is_isolation_supported()) + netdev_info(ndev, "SR-IOV not advertised by guests on the host supporting isolation\n"); + else + init_packet->msg.v2_msg.send_ndis_config.capability.sriov = 1; /* Teaming bit is needed to receive link speed updates */ init_packet->msg.v2_msg.send_ndis_config.capability.teaming = 1; @@ -609,6 +613,13 @@ static int netvsc_connect_vsp(struct hv_device *device, goto cleanup; } + if (hv_is_isolation_supported() && net_device->nvsp_version < NVSP_PROTOCOL_VERSION_61) { + netdev_err(ndev, "Invalid NVSP version 0x%x (expected >= 0x%x) from the host supporting isolation\n", + net_device->nvsp_version, NVSP_PROTOCOL_VERSION_61); + ret = -EPROTO; + goto cleanup; + } + pr_debug("Negotiated NVSP version:%x\n", net_device->nvsp_version); /* Send the ndis version */ @@ -1416,7 +1427,10 @@ static void netvsc_receive_inband(struct net_device *ndev, break; case NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION: - netvsc_send_vf(ndev, nvmsg, msglen); + if (hv_is_isolation_supported()) + netdev_err(ndev, "Ignore VF_ASSOCIATION msg from the host supporting isolation\n"); + else + netvsc_send_vf(ndev, nvmsg, msglen); break; } } diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 6db8d96a78eb..87aa62ee0368 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -1216,7 +1216,7 @@ static void hv_irq_unmask(struct irq_data *data) params = &hbus->retarget_msi_interrupt_params; memset(params, 0, sizeof(*params)); params->partition_id = HV_PARTITION_ID_SELF; - params->int_entry.source = 1; /* MSI(-X) */ + params->int_entry.source = HV_INTERRUPT_SOURCE_MSI; hv_set_msi_entry_from_desc(¶ms->int_entry.msi_entry, msi_desc); params->device_id = (hbus->hdev->dev_instance.b[5] << 24) | (hbus->hdev->dev_instance.b[4] << 16) | |