cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hyperv_drm_drv.c (7392B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright 2021 Microsoft
      4 */
      5
      6#include <linux/efi.h>
      7#include <linux/hyperv.h>
      8#include <linux/module.h>
      9#include <linux/pci.h>
     10
     11#include <drm/drm_aperture.h>
     12#include <drm/drm_atomic_helper.h>
     13#include <drm/drm_drv.h>
     14#include <drm/drm_fb_helper.h>
     15#include <drm/drm_gem_shmem_helper.h>
     16#include <drm/drm_simple_kms_helper.h>
     17
     18#include "hyperv_drm.h"
     19
     20#define DRIVER_NAME "hyperv_drm"
     21#define DRIVER_DESC "DRM driver for Hyper-V synthetic video device"
     22#define DRIVER_DATE "2020"
     23#define DRIVER_MAJOR 1
     24#define DRIVER_MINOR 0
     25
     26#define PCI_VENDOR_ID_MICROSOFT 0x1414
     27#define PCI_DEVICE_ID_HYPERV_VIDEO 0x5353
     28
     29DEFINE_DRM_GEM_FOPS(hv_fops);
     30
     31static struct drm_driver hyperv_driver = {
     32	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
     33
     34	.name		 = DRIVER_NAME,
     35	.desc		 = DRIVER_DESC,
     36	.date		 = DRIVER_DATE,
     37	.major		 = DRIVER_MAJOR,
     38	.minor		 = DRIVER_MINOR,
     39
     40	.fops		 = &hv_fops,
     41	DRM_GEM_SHMEM_DRIVER_OPS,
     42};
     43
     44static int hyperv_pci_probe(struct pci_dev *pdev,
     45			    const struct pci_device_id *ent)
     46{
     47	return 0;
     48}
     49
     50static void hyperv_pci_remove(struct pci_dev *pdev)
     51{
     52}
     53
     54static const struct pci_device_id hyperv_pci_tbl[] = {
     55	{
     56		.vendor = PCI_VENDOR_ID_MICROSOFT,
     57		.device = PCI_DEVICE_ID_HYPERV_VIDEO,
     58	},
     59	{ /* end of list */ }
     60};
     61
     62/*
     63 * PCI stub to support gen1 VM.
     64 */
     65static struct pci_driver hyperv_pci_driver = {
     66	.name =		KBUILD_MODNAME,
     67	.id_table =	hyperv_pci_tbl,
     68	.probe =	hyperv_pci_probe,
     69	.remove =	hyperv_pci_remove,
     70};
     71
     72static int hyperv_setup_gen1(struct hyperv_drm_device *hv)
     73{
     74	struct drm_device *dev = &hv->dev;
     75	struct pci_dev *pdev;
     76	int ret;
     77
     78	pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
     79			      PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
     80	if (!pdev) {
     81		drm_err(dev, "Unable to find PCI Hyper-V video\n");
     82		return -ENODEV;
     83	}
     84
     85	ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &hyperv_driver);
     86	if (ret) {
     87		drm_err(dev, "Not able to remove boot fb\n");
     88		return ret;
     89	}
     90
     91	if (pci_request_region(pdev, 0, DRIVER_NAME) != 0)
     92		drm_warn(dev, "Cannot request framebuffer, boot fb still active?\n");
     93
     94	if ((pdev->resource[0].flags & IORESOURCE_MEM) == 0) {
     95		drm_err(dev, "Resource at bar 0 is not IORESOURCE_MEM\n");
     96		ret = -ENODEV;
     97		goto error;
     98	}
     99
    100	hv->fb_base = pci_resource_start(pdev, 0);
    101	hv->fb_size = pci_resource_len(pdev, 0);
    102	if (!hv->fb_base) {
    103		drm_err(dev, "Resource not available\n");
    104		ret = -ENODEV;
    105		goto error;
    106	}
    107
    108	hv->fb_size = min(hv->fb_size,
    109			  (unsigned long)(hv->mmio_megabytes * 1024 * 1024));
    110	hv->vram = devm_ioremap(&pdev->dev, hv->fb_base, hv->fb_size);
    111	if (!hv->vram) {
    112		drm_err(dev, "Failed to map vram\n");
    113		ret = -ENOMEM;
    114	}
    115
    116error:
    117	pci_dev_put(pdev);
    118	return ret;
    119}
    120
    121static int hyperv_setup_gen2(struct hyperv_drm_device *hv,
    122			     struct hv_device *hdev)
    123{
    124	struct drm_device *dev = &hv->dev;
    125	int ret;
    126
    127	drm_aperture_remove_conflicting_framebuffers(screen_info.lfb_base,
    128						     screen_info.lfb_size,
    129						     false,
    130						     &hyperv_driver);
    131
    132	hv->fb_size = (unsigned long)hv->mmio_megabytes * 1024 * 1024;
    133
    134	ret = vmbus_allocate_mmio(&hv->mem, hdev, 0, -1, hv->fb_size, 0x100000,
    135				  true);
    136	if (ret) {
    137		drm_err(dev, "Failed to allocate mmio\n");
    138		return -ENOMEM;
    139	}
    140
    141	/*
    142	 * Map the VRAM cacheable for performance. This is also required for VM
    143	 * connect to display properly for ARM64 Linux VM, as the host also maps
    144	 * the VRAM cacheable.
    145	 */
    146	hv->vram = ioremap_cache(hv->mem->start, hv->fb_size);
    147	if (!hv->vram) {
    148		drm_err(dev, "Failed to map vram\n");
    149		ret = -ENOMEM;
    150		goto error;
    151	}
    152
    153	hv->fb_base = hv->mem->start;
    154	return 0;
    155
    156error:
    157	vmbus_free_mmio(hv->mem->start, hv->fb_size);
    158	return ret;
    159}
    160
    161static int hyperv_vmbus_probe(struct hv_device *hdev,
    162			      const struct hv_vmbus_device_id *dev_id)
    163{
    164	struct hyperv_drm_device *hv;
    165	struct drm_device *dev;
    166	int ret;
    167
    168	hv = devm_drm_dev_alloc(&hdev->device, &hyperv_driver,
    169				struct hyperv_drm_device, dev);
    170	if (IS_ERR(hv))
    171		return PTR_ERR(hv);
    172
    173	dev = &hv->dev;
    174	init_completion(&hv->wait);
    175	hv_set_drvdata(hdev, hv);
    176	hv->hdev = hdev;
    177
    178	ret = hyperv_connect_vsp(hdev);
    179	if (ret) {
    180		drm_err(dev, "Failed to connect to vmbus.\n");
    181		goto err_hv_set_drv_data;
    182	}
    183
    184	if (efi_enabled(EFI_BOOT))
    185		ret = hyperv_setup_gen2(hv, hdev);
    186	else
    187		ret = hyperv_setup_gen1(hv);
    188
    189	if (ret)
    190		goto err_vmbus_close;
    191
    192	/*
    193	 * Should be done only once during init and resume. Failing to update
    194	 * vram location is not fatal. Device will update dirty area till
    195	 * preferred resolution only.
    196	 */
    197	ret = hyperv_update_vram_location(hdev, hv->fb_base);
    198	if (ret)
    199		drm_warn(dev, "Failed to update vram location.\n");
    200
    201	hv->dirt_needed = true;
    202
    203	ret = hyperv_mode_config_init(hv);
    204	if (ret)
    205		goto err_vmbus_close;
    206
    207	ret = drm_dev_register(dev, 0);
    208	if (ret) {
    209		drm_err(dev, "Failed to register drm driver.\n");
    210		goto err_vmbus_close;
    211	}
    212
    213	drm_fbdev_generic_setup(dev, 0);
    214
    215	return 0;
    216
    217err_vmbus_close:
    218	vmbus_close(hdev->channel);
    219err_hv_set_drv_data:
    220	hv_set_drvdata(hdev, NULL);
    221	return ret;
    222}
    223
    224static int hyperv_vmbus_remove(struct hv_device *hdev)
    225{
    226	struct drm_device *dev = hv_get_drvdata(hdev);
    227	struct hyperv_drm_device *hv = to_hv(dev);
    228	struct pci_dev *pdev;
    229
    230	drm_dev_unplug(dev);
    231	drm_atomic_helper_shutdown(dev);
    232	vmbus_close(hdev->channel);
    233	hv_set_drvdata(hdev, NULL);
    234
    235	/*
    236	 * Free allocated MMIO memory only on Gen2 VMs.
    237	 * On Gen1 VMs, release the PCI device
    238	 */
    239	if (efi_enabled(EFI_BOOT)) {
    240		vmbus_free_mmio(hv->mem->start, hv->fb_size);
    241	} else {
    242		pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT,
    243				      PCI_DEVICE_ID_HYPERV_VIDEO, NULL);
    244		if (!pdev) {
    245			drm_err(dev, "Unable to find PCI Hyper-V video\n");
    246			return -ENODEV;
    247		}
    248		pci_release_region(pdev, 0);
    249		pci_dev_put(pdev);
    250	}
    251
    252	return 0;
    253}
    254
    255static int hyperv_vmbus_suspend(struct hv_device *hdev)
    256{
    257	struct drm_device *dev = hv_get_drvdata(hdev);
    258	int ret;
    259
    260	ret = drm_mode_config_helper_suspend(dev);
    261	if (ret)
    262		return ret;
    263
    264	vmbus_close(hdev->channel);
    265
    266	return 0;
    267}
    268
    269static int hyperv_vmbus_resume(struct hv_device *hdev)
    270{
    271	struct drm_device *dev = hv_get_drvdata(hdev);
    272	struct hyperv_drm_device *hv = to_hv(dev);
    273	int ret;
    274
    275	ret = hyperv_connect_vsp(hdev);
    276	if (ret)
    277		return ret;
    278
    279	ret = hyperv_update_vram_location(hdev, hv->fb_base);
    280	if (ret)
    281		return ret;
    282
    283	return drm_mode_config_helper_resume(dev);
    284}
    285
    286static const struct hv_vmbus_device_id hyperv_vmbus_tbl[] = {
    287	/* Synthetic Video Device GUID */
    288	{HV_SYNTHVID_GUID},
    289	{}
    290};
    291
    292static struct hv_driver hyperv_hv_driver = {
    293	.name = KBUILD_MODNAME,
    294	.id_table = hyperv_vmbus_tbl,
    295	.probe = hyperv_vmbus_probe,
    296	.remove = hyperv_vmbus_remove,
    297	.suspend = hyperv_vmbus_suspend,
    298	.resume = hyperv_vmbus_resume,
    299	.driver = {
    300		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
    301	},
    302};
    303
    304static int __init hyperv_init(void)
    305{
    306	int ret;
    307
    308	if (drm_firmware_drivers_only())
    309		return -ENODEV;
    310
    311	ret = pci_register_driver(&hyperv_pci_driver);
    312	if (ret != 0)
    313		return ret;
    314
    315	return vmbus_driver_register(&hyperv_hv_driver);
    316}
    317
    318static void __exit hyperv_exit(void)
    319{
    320	vmbus_driver_unregister(&hyperv_hv_driver);
    321	pci_unregister_driver(&hyperv_pci_driver);
    322}
    323
    324module_init(hyperv_init);
    325module_exit(hyperv_exit);
    326
    327MODULE_DEVICE_TABLE(pci, hyperv_pci_tbl);
    328MODULE_DEVICE_TABLE(vmbus, hyperv_vmbus_tbl);
    329MODULE_LICENSE("GPL");
    330MODULE_AUTHOR("Deepak Rawat <drawat.floss@gmail.com>");
    331MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device");