#include "i915_drv.h"
 #include "gvt.h"
+#include <linux/vfio.h>
+#include <linux/mdev.h>
 
 struct intel_gvt_host intel_gvt_host;
 
        [INTEL_GVT_HYPERVISOR_KVM] = "KVM",
 };
 
+static struct intel_vgpu_type *intel_gvt_find_vgpu_type(struct intel_gvt *gvt,
+               const char *name)
+{
+       int i;
+       struct intel_vgpu_type *t;
+       const char *driver_name = dev_driver_string(
+                       &gvt->dev_priv->drm.pdev->dev);
+
+       for (i = 0; i < gvt->num_types; i++) {
+               t = &gvt->types[i];
+               if (!strncmp(t->name, name + strlen(driver_name) + 1,
+                       sizeof(t->name)))
+                       return t;
+       }
+
+       return NULL;
+}
+
+static ssize_t available_instances_show(struct kobject *kobj,
+                                       struct device *dev, char *buf)
+{
+       struct intel_vgpu_type *type;
+       unsigned int num = 0;
+       void *gvt = kdev_to_i915(dev)->gvt;
+
+       type = intel_gvt_find_vgpu_type(gvt, kobject_name(kobj));
+       if (!type)
+               num = 0;
+       else
+               num = type->avail_instance;
+
+       return sprintf(buf, "%u\n", num);
+}
+
+static ssize_t device_api_show(struct kobject *kobj, struct device *dev,
+               char *buf)
+{
+       return sprintf(buf, "%s\n", VFIO_DEVICE_API_PCI_STRING);
+}
+
+static ssize_t description_show(struct kobject *kobj, struct device *dev,
+               char *buf)
+{
+       struct intel_vgpu_type *type;
+       void *gvt = kdev_to_i915(dev)->gvt;
+
+       type = intel_gvt_find_vgpu_type(gvt, kobject_name(kobj));
+       if (!type)
+               return 0;
+
+       return sprintf(buf, "low_gm_size: %dMB\nhigh_gm_size: %dMB\n"
+                      "fence: %d\nresolution: %s\n"
+                      "weight: %d\n",
+                      BYTES_TO_MB(type->low_gm_size),
+                      BYTES_TO_MB(type->high_gm_size),
+                      type->fence, vgpu_edid_str(type->resolution),
+                      type->weight);
+}
+
+static MDEV_TYPE_ATTR_RO(available_instances);
+static MDEV_TYPE_ATTR_RO(device_api);
+static MDEV_TYPE_ATTR_RO(description);
+
+static struct attribute *gvt_type_attrs[] = {
+       &mdev_type_attr_available_instances.attr,
+       &mdev_type_attr_device_api.attr,
+       &mdev_type_attr_description.attr,
+       NULL,
+};
+
+static struct attribute_group *gvt_vgpu_type_groups[] = {
+       [0 ... NR_MAX_INTEL_VGPU_TYPES - 1] = NULL,
+};
+
+static bool intel_get_gvt_attrs(struct attribute ***type_attrs,
+               struct attribute_group ***intel_vgpu_type_groups)
+{
+       *type_attrs = gvt_type_attrs;
+       *intel_vgpu_type_groups = gvt_vgpu_type_groups;
+       return true;
+}
+
+static bool intel_gvt_init_vgpu_type_groups(struct intel_gvt *gvt)
+{
+       int i, j;
+       struct intel_vgpu_type *type;
+       struct attribute_group *group;
+
+       for (i = 0; i < gvt->num_types; i++) {
+               type = &gvt->types[i];
+
+               group = kzalloc(sizeof(struct attribute_group), GFP_KERNEL);
+               if (WARN_ON(!group))
+                       goto unwind;
+
+               group->name = type->name;
+               group->attrs = gvt_type_attrs;
+               gvt_vgpu_type_groups[i] = group;
+       }
+
+       return true;
+
+unwind:
+       for (j = 0; j < i; j++) {
+               group = gvt_vgpu_type_groups[j];
+               kfree(group);
+       }
+
+       return false;
+}
+
+static void intel_gvt_cleanup_vgpu_type_groups(struct intel_gvt *gvt)
+{
+       int i;
+       struct attribute_group *group;
+
+       for (i = 0; i < gvt->num_types; i++) {
+               group = gvt_vgpu_type_groups[i];
+               gvt_vgpu_type_groups[i] = NULL;
+               kfree(group);
+       }
+}
+
 static const struct intel_gvt_ops intel_gvt_ops = {
        .emulate_cfg_read = intel_vgpu_emulate_cfg_read,
        .emulate_cfg_write = intel_vgpu_emulate_cfg_write,
        .vgpu_reset = intel_gvt_reset_vgpu,
        .vgpu_activate = intel_gvt_activate_vgpu,
        .vgpu_deactivate = intel_gvt_deactivate_vgpu,
+       .gvt_find_vgpu_type = intel_gvt_find_vgpu_type,
+       .get_gvt_attrs = intel_get_gvt_attrs,
 };
 
 /**
        intel_gvt_free_firmware(gvt);
 
        intel_gvt_hypervisor_host_exit(&dev_priv->drm.pdev->dev, gvt);
+       intel_gvt_cleanup_vgpu_type_groups(gvt);
        intel_gvt_clean_vgpu_types(gvt);
 
        idr_destroy(&gvt->vgpu_idr);
        if (ret)
                goto out_clean_thread;
 
+       ret = intel_gvt_init_vgpu_type_groups(gvt);
+       if (ret == false) {
+               gvt_err("failed to init vgpu type groups: %d\n", ret);
+               goto out_clean_types;
+       }
+
        ret = intel_gvt_hypervisor_host_init(&dev_priv->drm.pdev->dev, gvt,
                                &intel_gvt_ops);
        if (ret) {