Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/xe: Support for mmap-ing mmio regions

Allow the driver to expose hardware register spaces to userspace
through GEM objects with fake mmap offsets. This can be useful
for userspace-firmware communication, debugging, etc.

v2: Minor doc fix (CI)
v3: Enforce MAP_SHARED (Tejas)
Add fault handler with dummy page (Tejas, Matt Auld)
Store physical address instead of xe_mmio in the GEM object (MattB)
v4: Separate xe_mmio_gem from xe_mmio and make it private (MattB)

Signed-off-by: Ilia Levi <ilia.levi@intel.com>
Reviewed-by: Matthew Brost <matthew.brost@intel.com>
Reviewed-by: Tejas Upadhyay <tejas.upadhyay@intel.com>
Link: https://lore.kernel.org/r/20250714122658.1803-1-ilia.levi@intel.com
Signed-off-by: Lucas De Marchi <lucas.demarchi@intel.com>

authored by

Ilia Levi and committed by
Lucas De Marchi
1ffcf8b8 ca33cd27

+247
+1
drivers/gpu/drm/xe/Makefile
··· 87 87 xe_lrc.o \ 88 88 xe_migrate.o \ 89 89 xe_mmio.o \ 90 + xe_mmio_gem.o \ 90 91 xe_mocs.o \ 91 92 xe_module.o \ 92 93 xe_nvm.o \
+226
drivers/gpu/drm/xe/xe_mmio_gem.c
··· 1 + // SPDX-License-Identifier: MIT 2 + /* 3 + * Copyright © 2025 Intel Corporation 4 + */ 5 + 6 + #include "xe_mmio_gem.h" 7 + 8 + #include <drm/drm_drv.h> 9 + #include <drm/drm_gem.h> 10 + #include <drm/drm_managed.h> 11 + 12 + #include "xe_device_types.h" 13 + 14 + /** 15 + * DOC: Exposing MMIO regions to userspace 16 + * 17 + * In certain cases, the driver may allow userspace to mmap a portion of the hardware registers. 18 + * 19 + * This can be done as follows: 20 + * 1. Call xe_mmio_gem_create() to create a GEM object with an mmap-able fake offset. 21 + * 2. Use xe_mmio_gem_mmap_offset() on the created GEM object to retrieve the fake offset. 22 + * 3. Provide the fake offset to userspace. 23 + * 4. Userspace can call mmap with the fake offset. The length provided to mmap 24 + * must match the size of the GEM object. 25 + * 5. When the region is no longer needed, call xe_mmio_gem_destroy() to release the GEM object. 26 + * 27 + * NOTE: The exposed MMIO region must be page-aligned with regards to its BAR offset and size. 28 + * 29 + * WARNING: Exposing MMIO regions to userspace can have security and stability implications. 30 + * Make sure not to expose any sensitive registers. 31 + */ 32 + 33 + static void xe_mmio_gem_free(struct drm_gem_object *); 34 + static int xe_mmio_gem_mmap(struct drm_gem_object *, struct vm_area_struct *); 35 + static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *); 36 + 37 + struct xe_mmio_gem { 38 + struct drm_gem_object base; 39 + phys_addr_t phys_addr; 40 + }; 41 + 42 + static const struct vm_operations_struct vm_ops = { 43 + .open = drm_gem_vm_open, 44 + .close = drm_gem_vm_close, 45 + .fault = xe_mmio_gem_vm_fault, 46 + }; 47 + 48 + static const struct drm_gem_object_funcs xe_mmio_gem_funcs = { 49 + .free = xe_mmio_gem_free, 50 + .mmap = xe_mmio_gem_mmap, 51 + .vm_ops = &vm_ops, 52 + }; 53 + 54 + static inline struct xe_mmio_gem *to_xe_mmio_gem(struct drm_gem_object *obj) 55 + { 56 + return container_of(obj, struct xe_mmio_gem, base); 57 + } 58 + 59 + /** 60 + * xe_mmio_gem_create - Expose an MMIO region to userspace 61 + * @xe: The xe device 62 + * @file: DRM file descriptor 63 + * @phys_addr: Start of the exposed MMIO region 64 + * @size: The size of the exposed MMIO region 65 + * 66 + * This function creates a GEM object that exposes an MMIO region with an mmap-able 67 + * fake offset. 68 + * 69 + * See: "Exposing MMIO regions to userspace" 70 + */ 71 + struct xe_mmio_gem *xe_mmio_gem_create(struct xe_device *xe, struct drm_file *file, 72 + phys_addr_t phys_addr, size_t size) 73 + { 74 + struct xe_mmio_gem *obj; 75 + struct drm_gem_object *base; 76 + int err; 77 + 78 + if ((phys_addr % PAGE_SIZE != 0) || (size % PAGE_SIZE != 0)) 79 + return ERR_PTR(-EINVAL); 80 + 81 + obj = kzalloc(sizeof(*obj), GFP_KERNEL); 82 + if (!obj) 83 + return ERR_PTR(-ENOMEM); 84 + 85 + base = &obj->base; 86 + base->funcs = &xe_mmio_gem_funcs; 87 + obj->phys_addr = phys_addr; 88 + 89 + drm_gem_private_object_init(&xe->drm, base, size); 90 + 91 + err = drm_gem_create_mmap_offset(base); 92 + if (err) 93 + goto free_gem; 94 + 95 + err = drm_vma_node_allow(&base->vma_node, file); 96 + if (err) 97 + goto free_gem; 98 + 99 + return obj; 100 + 101 + free_gem: 102 + xe_mmio_gem_free(base); 103 + return ERR_PTR(err); 104 + } 105 + 106 + /** 107 + * xe_mmio_gem_mmap_offset - Return the mmap-able fake offset 108 + * @gem: the GEM object created with xe_mmio_gem_create() 109 + * 110 + * This function returns the mmap-able fake offset allocated during 111 + * xe_mmio_gem_create(). 112 + * 113 + * See: "Exposing MMIO regions to userspace" 114 + */ 115 + u64 xe_mmio_gem_mmap_offset(struct xe_mmio_gem *gem) 116 + { 117 + return drm_vma_node_offset_addr(&gem->base.vma_node); 118 + } 119 + 120 + static void xe_mmio_gem_free(struct drm_gem_object *base) 121 + { 122 + struct xe_mmio_gem *obj = to_xe_mmio_gem(base); 123 + 124 + drm_gem_object_release(base); 125 + kfree(obj); 126 + } 127 + 128 + /** 129 + * xe_mmio_gem_destroy - Destroy the GEM object that exposes an MMIO region 130 + * @gem: the GEM object to destroy 131 + * 132 + * This function releases resources associated with the GEM object created by 133 + * xe_mmio_gem_create(). 134 + * 135 + * See: "Exposing MMIO regions to userspace" 136 + */ 137 + void xe_mmio_gem_destroy(struct xe_mmio_gem *gem) 138 + { 139 + xe_mmio_gem_free(&gem->base); 140 + } 141 + 142 + static int xe_mmio_gem_mmap(struct drm_gem_object *base, struct vm_area_struct *vma) 143 + { 144 + if (vma->vm_end - vma->vm_start != base->size) 145 + return -EINVAL; 146 + 147 + if ((vma->vm_flags & VM_SHARED) == 0) 148 + return -EINVAL; 149 + 150 + /* Set vm_pgoff (used as a fake buffer offset by DRM) to 0 */ 151 + vma->vm_pgoff = 0; 152 + vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma->vm_flags)); 153 + vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP | 154 + VM_DONTCOPY | VM_NORESERVE); 155 + 156 + /* Defer actual mapping to the fault handler. */ 157 + return 0; 158 + } 159 + 160 + static void xe_mmio_gem_release_dummy_page(struct drm_device *dev, void *res) 161 + { 162 + __free_page((struct page *)res); 163 + } 164 + 165 + static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_area_struct *vma) 166 + { 167 + struct drm_gem_object *base = vma->vm_private_data; 168 + struct drm_device *dev = base->dev; 169 + vm_fault_t ret = VM_FAULT_NOPAGE; 170 + struct page *page; 171 + unsigned long pfn; 172 + unsigned long i; 173 + 174 + page = alloc_page(GFP_KERNEL | __GFP_ZERO); 175 + if (!page) 176 + return VM_FAULT_OOM; 177 + 178 + if (drmm_add_action_or_reset(dev, xe_mmio_gem_release_dummy_page, page)) 179 + return VM_FAULT_OOM; 180 + 181 + pfn = page_to_pfn(page); 182 + 183 + /* Map the entire VMA to the same dummy page */ 184 + for (i = 0; i < base->size; i += PAGE_SIZE) { 185 + unsigned long addr = vma->vm_start + i; 186 + 187 + ret = vmf_insert_pfn(vma, addr, pfn); 188 + if (ret & VM_FAULT_ERROR) 189 + break; 190 + } 191 + 192 + return ret; 193 + } 194 + 195 + static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf) 196 + { 197 + struct vm_area_struct *vma = vmf->vma; 198 + struct drm_gem_object *base = vma->vm_private_data; 199 + struct xe_mmio_gem *obj = to_xe_mmio_gem(base); 200 + struct drm_device *dev = base->dev; 201 + vm_fault_t ret = VM_FAULT_NOPAGE; 202 + unsigned long i; 203 + int idx; 204 + 205 + if (!drm_dev_enter(dev, &idx)) { 206 + /* 207 + * Provide a dummy page to avoid SIGBUS for events such as hot-unplug. 208 + * This gives the userspace the option to recover instead of crashing. 209 + * It is assumed the userspace will receive the notification via some 210 + * other channel (e.g. drm uevent). 211 + */ 212 + return xe_mmio_gem_vm_fault_dummy_page(vma); 213 + } 214 + 215 + for (i = 0; i < base->size; i += PAGE_SIZE) { 216 + unsigned long addr = vma->vm_start + i; 217 + unsigned long phys_addr = obj->phys_addr + i; 218 + 219 + ret = vmf_insert_pfn(vma, addr, PHYS_PFN(phys_addr)); 220 + if (ret & VM_FAULT_ERROR) 221 + break; 222 + } 223 + 224 + drm_dev_exit(idx); 225 + return ret; 226 + }
+20
drivers/gpu/drm/xe/xe_mmio_gem.h
··· 1 + /* SPDX-License-Identifier: MIT */ 2 + /* 3 + * Copyright © 2025 Intel Corporation 4 + */ 5 + 6 + #ifndef _XE_MMIO_GEM_H_ 7 + #define _XE_MMIO_GEM_H_ 8 + 9 + #include <linux/types.h> 10 + 11 + struct drm_file; 12 + struct xe_device; 13 + struct xe_mmio_gem; 14 + 15 + struct xe_mmio_gem *xe_mmio_gem_create(struct xe_device *xe, struct drm_file *file, 16 + phys_addr_t phys_addr, size_t size); 17 + u64 xe_mmio_gem_mmap_offset(struct xe_mmio_gem *gem); 18 + void xe_mmio_gem_destroy(struct xe_mmio_gem *gem); 19 + 20 + #endif /* _XE_MMIO_GEM_H_ */