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

drm_aperture.c (10619B)


      1// SPDX-License-Identifier: MIT
      2
      3#include <linux/device.h>
      4#include <linux/fb.h>
      5#include <linux/list.h>
      6#include <linux/mutex.h>
      7#include <linux/pci.h>
      8#include <linux/platform_device.h> /* for firmware helpers */
      9#include <linux/slab.h>
     10#include <linux/types.h>
     11#include <linux/vgaarb.h>
     12
     13#include <drm/drm_aperture.h>
     14#include <drm/drm_drv.h>
     15#include <drm/drm_print.h>
     16
     17/**
     18 * DOC: overview
     19 *
     20 * A graphics device might be supported by different drivers, but only one
     21 * driver can be active at any given time. Many systems load a generic
     22 * graphics drivers, such as EFI-GOP or VESA, early during the boot process.
     23 * During later boot stages, they replace the generic driver with a dedicated,
     24 * hardware-specific driver. To take over the device the dedicated driver
     25 * first has to remove the generic driver. DRM aperture functions manage
     26 * ownership of DRM framebuffer memory and hand-over between drivers.
     27 *
     28 * DRM drivers should call drm_aperture_remove_conflicting_framebuffers()
     29 * at the top of their probe function. The function removes any generic
     30 * driver that is currently associated with the given framebuffer memory.
     31 * If the framebuffer is located at PCI BAR 0, the rsp code looks as in the
     32 * example given below.
     33 *
     34 * .. code-block:: c
     35 *
     36 *	static const struct drm_driver example_driver = {
     37 *		...
     38 *	};
     39 *
     40 *	static int remove_conflicting_framebuffers(struct pci_dev *pdev)
     41 *	{
     42 *		bool primary = false;
     43 *		resource_size_t base, size;
     44 *		int ret;
     45 *
     46 *		base = pci_resource_start(pdev, 0);
     47 *		size = pci_resource_len(pdev, 0);
     48 *	#ifdef CONFIG_X86
     49 *		primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
     50 *	#endif
     51 *
     52 *		return drm_aperture_remove_conflicting_framebuffers(base, size, primary,
     53 *		                                                    &example_driver);
     54 *	}
     55 *
     56 *	static int probe(struct pci_dev *pdev)
     57 *	{
     58 *		int ret;
     59 *
     60 *		// Remove any generic drivers...
     61 *		ret = remove_conflicting_framebuffers(pdev);
     62 *		if (ret)
     63 *			return ret;
     64 *
     65 *		// ... and initialize the hardware.
     66 *		...
     67 *
     68 *		drm_dev_register();
     69 *
     70 *		return 0;
     71 *	}
     72 *
     73 * PCI device drivers should call
     74 * drm_aperture_remove_conflicting_pci_framebuffers() and let it detect the
     75 * framebuffer apertures automatically. Device drivers without knowledge of
     76 * the framebuffer's location shall call drm_aperture_remove_framebuffers(),
     77 * which removes all drivers for known framebuffer.
     78 *
     79 * Drivers that are susceptible to being removed by other drivers, such as
     80 * generic EFI or VESA drivers, have to register themselves as owners of their
     81 * given framebuffer memory. Ownership of the framebuffer memory is achieved
     82 * by calling devm_aperture_acquire_from_firmware(). On success, the driver
     83 * is the owner of the framebuffer range. The function fails if the
     84 * framebuffer is already by another driver. See below for an example.
     85 *
     86 * .. code-block:: c
     87 *
     88 *	static int acquire_framebuffers(struct drm_device *dev, struct platform_device *pdev)
     89 *	{
     90 *		resource_size_t base, size;
     91 *
     92 *		mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
     93 *		if (!mem)
     94 *			return -EINVAL;
     95 *		base = mem->start;
     96 *		size = resource_size(mem);
     97 *
     98 *		return devm_acquire_aperture_from_firmware(dev, base, size);
     99 *	}
    100 *
    101 *	static int probe(struct platform_device *pdev)
    102 *	{
    103 *		struct drm_device *dev;
    104 *		int ret;
    105 *
    106 *		// ... Initialize the device...
    107 *		dev = devm_drm_dev_alloc();
    108 *		...
    109 *
    110 *		// ... and acquire ownership of the framebuffer.
    111 *		ret = acquire_framebuffers(dev, pdev);
    112 *		if (ret)
    113 *			return ret;
    114 *
    115 *		drm_dev_register(dev, 0);
    116 *
    117 *		return 0;
    118 *	}
    119 *
    120 * The generic driver is now subject to forced removal by other drivers. This
    121 * only works for platform drivers that support hot unplug.
    122 * When a driver calls drm_aperture_remove_conflicting_framebuffers() et al
    123 * for the registered framebuffer range, the aperture helpers call
    124 * platform_device_unregister() and the generic driver unloads itself. It
    125 * may not access the device's registers, framebuffer memory, ROM, etc
    126 * afterwards.
    127 */
    128
    129struct drm_aperture {
    130	struct drm_device *dev;
    131	resource_size_t base;
    132	resource_size_t size;
    133	struct list_head lh;
    134	void (*detach)(struct drm_device *dev);
    135};
    136
    137static LIST_HEAD(drm_apertures);
    138static DEFINE_MUTEX(drm_apertures_lock);
    139
    140static bool overlap(resource_size_t base1, resource_size_t end1,
    141		    resource_size_t base2, resource_size_t end2)
    142{
    143	return (base1 < end2) && (end1 > base2);
    144}
    145
    146static void devm_aperture_acquire_release(void *data)
    147{
    148	struct drm_aperture *ap = data;
    149	bool detached = !ap->dev;
    150
    151	if (detached)
    152		return;
    153
    154	mutex_lock(&drm_apertures_lock);
    155	list_del(&ap->lh);
    156	mutex_unlock(&drm_apertures_lock);
    157}
    158
    159static int devm_aperture_acquire(struct drm_device *dev,
    160				 resource_size_t base, resource_size_t size,
    161				 void (*detach)(struct drm_device *))
    162{
    163	size_t end = base + size;
    164	struct list_head *pos;
    165	struct drm_aperture *ap;
    166
    167	mutex_lock(&drm_apertures_lock);
    168
    169	list_for_each(pos, &drm_apertures) {
    170		ap = container_of(pos, struct drm_aperture, lh);
    171		if (overlap(base, end, ap->base, ap->base + ap->size)) {
    172			mutex_unlock(&drm_apertures_lock);
    173			return -EBUSY;
    174		}
    175	}
    176
    177	ap = devm_kzalloc(dev->dev, sizeof(*ap), GFP_KERNEL);
    178	if (!ap) {
    179		mutex_unlock(&drm_apertures_lock);
    180		return -ENOMEM;
    181	}
    182
    183	ap->dev = dev;
    184	ap->base = base;
    185	ap->size = size;
    186	ap->detach = detach;
    187	INIT_LIST_HEAD(&ap->lh);
    188
    189	list_add(&ap->lh, &drm_apertures);
    190
    191	mutex_unlock(&drm_apertures_lock);
    192
    193	return devm_add_action_or_reset(dev->dev, devm_aperture_acquire_release, ap);
    194}
    195
    196static void drm_aperture_detach_firmware(struct drm_device *dev)
    197{
    198	struct platform_device *pdev = to_platform_device(dev->dev);
    199
    200	/*
    201	 * Remove the device from the device hierarchy. This is the right thing
    202	 * to do for firmware-based DRM drivers, such as EFI, VESA or VGA. After
    203	 * the new driver takes over the hardware, the firmware device's state
    204	 * will be lost.
    205	 *
    206	 * For non-platform devices, a new callback would be required.
    207	 *
    208	 * If the aperture helpers ever need to handle native drivers, this call
    209	 * would only have to unplug the DRM device, so that the hardware device
    210	 * stays around after detachment.
    211	 */
    212	platform_device_unregister(pdev);
    213}
    214
    215/**
    216 * devm_aperture_acquire_from_firmware - Acquires ownership of a firmware framebuffer
    217 *                                       on behalf of a DRM driver.
    218 * @dev:	the DRM device to own the framebuffer memory
    219 * @base:	the framebuffer's byte offset in physical memory
    220 * @size:	the framebuffer size in bytes
    221 *
    222 * Installs the given device as the new owner of the framebuffer. The function
    223 * expects the framebuffer to be provided by a platform device that has been
    224 * set up by firmware. Firmware can be any generic interface, such as EFI,
    225 * VESA, VGA, etc. If the native hardware driver takes over ownership of the
    226 * framebuffer range, the firmware state gets lost. Aperture helpers will then
    227 * unregister the platform device automatically. Acquired apertures are
    228 * released automatically if the underlying device goes away.
    229 *
    230 * The function fails if the framebuffer range, or parts of it, is currently
    231 * owned by another driver. To evict current owners, callers should use
    232 * drm_aperture_remove_conflicting_framebuffers() et al. before calling this
    233 * function. The function also fails if the given device is not a platform
    234 * device.
    235 *
    236 * Returns:
    237 * 0 on success, or a negative errno value otherwise.
    238 */
    239int devm_aperture_acquire_from_firmware(struct drm_device *dev, resource_size_t base,
    240					resource_size_t size)
    241{
    242	if (drm_WARN_ON(dev, !dev_is_platform(dev->dev)))
    243		return -EINVAL;
    244
    245	return devm_aperture_acquire(dev, base, size, drm_aperture_detach_firmware);
    246}
    247EXPORT_SYMBOL(devm_aperture_acquire_from_firmware);
    248
    249static void drm_aperture_detach_drivers(resource_size_t base, resource_size_t size)
    250{
    251	resource_size_t end = base + size;
    252	struct list_head *pos, *n;
    253
    254	mutex_lock(&drm_apertures_lock);
    255
    256	list_for_each_safe(pos, n, &drm_apertures) {
    257		struct drm_aperture *ap =
    258			container_of(pos, struct drm_aperture, lh);
    259		struct drm_device *dev = ap->dev;
    260
    261		if (WARN_ON_ONCE(!dev))
    262			continue;
    263
    264		if (!overlap(base, end, ap->base, ap->base + ap->size))
    265			continue;
    266
    267		ap->dev = NULL; /* detach from device */
    268		list_del(&ap->lh);
    269
    270		ap->detach(dev);
    271	}
    272
    273	mutex_unlock(&drm_apertures_lock);
    274}
    275
    276/**
    277 * drm_aperture_remove_conflicting_framebuffers - remove existing framebuffers in the given range
    278 * @base: the aperture's base address in physical memory
    279 * @size: aperture size in bytes
    280 * @primary: also kick vga16fb if present
    281 * @req_driver: requesting DRM driver
    282 *
    283 * This function removes graphics device drivers which use memory range described by
    284 * @base and @size.
    285 *
    286 * Returns:
    287 * 0 on success, or a negative errno code otherwise
    288 */
    289int drm_aperture_remove_conflicting_framebuffers(resource_size_t base, resource_size_t size,
    290						 bool primary, const struct drm_driver *req_driver)
    291{
    292#if IS_REACHABLE(CONFIG_FB)
    293	struct apertures_struct *a;
    294	int ret;
    295
    296	a = alloc_apertures(1);
    297	if (!a)
    298		return -ENOMEM;
    299
    300	a->ranges[0].base = base;
    301	a->ranges[0].size = size;
    302
    303	ret = remove_conflicting_framebuffers(a, req_driver->name, primary);
    304	kfree(a);
    305
    306	if (ret)
    307		return ret;
    308#endif
    309
    310	drm_aperture_detach_drivers(base, size);
    311
    312	return 0;
    313}
    314EXPORT_SYMBOL(drm_aperture_remove_conflicting_framebuffers);
    315
    316/**
    317 * drm_aperture_remove_conflicting_pci_framebuffers - remove existing framebuffers for PCI devices
    318 * @pdev: PCI device
    319 * @req_driver: requesting DRM driver
    320 *
    321 * This function removes graphics device drivers using memory range configured
    322 * for any of @pdev's memory bars. The function assumes that PCI device with
    323 * shadowed ROM drives a primary display and so kicks out vga16fb.
    324 *
    325 * Returns:
    326 * 0 on success, or a negative errno code otherwise
    327 */
    328int drm_aperture_remove_conflicting_pci_framebuffers(struct pci_dev *pdev,
    329						     const struct drm_driver *req_driver)
    330{
    331	resource_size_t base, size;
    332	int bar, ret;
    333
    334	/*
    335	 * WARNING: Apparently we must kick fbdev drivers before vgacon,
    336	 * otherwise the vga fbdev driver falls over.
    337	 */
    338#if IS_REACHABLE(CONFIG_FB)
    339	ret = remove_conflicting_pci_framebuffers(pdev, req_driver->name);
    340	if (ret)
    341		return ret;
    342#endif
    343	ret = vga_remove_vgacon(pdev);
    344	if (ret)
    345		return ret;
    346
    347	for (bar = 0; bar < PCI_STD_NUM_BARS; ++bar) {
    348		if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
    349			continue;
    350		base = pci_resource_start(pdev, bar);
    351		size = pci_resource_len(pdev, bar);
    352		drm_aperture_detach_drivers(base, size);
    353	}
    354
    355	return 0;
    356}
    357EXPORT_SYMBOL(drm_aperture_remove_conflicting_pci_framebuffers);