crypto: mips/poly1305 - enable for all MIPS processors
[linux-2.6-microblaze.git] / drivers / hv / channel_mgmt.c
index 1d44bb6..f0ed730 100644 (file)
@@ -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