aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linuxkpi/common/src
diff options
context:
space:
mode:
authorJean-Sébastien Pédron <dumbbell@FreeBSD.org>2023-01-14 12:22:19 +0000
committerJean-Sébastien Pédron <dumbbell@FreeBSD.org>2023-01-25 21:26:54 +0000
commitb99bc862324526b3ee6fad335618cbe85ad9e11e (patch)
tree94d2dbce5488a341e64ed1a1c7e6f82e42fcab9b /sys/compat/linuxkpi/common/src
parent4fee6659c42c264e17dad7625f8663a45594d46b (diff)
downloadsrc-b99bc862324526b3ee6fad335618cbe85ad9e11e.tar.gz
src-b99bc862324526b3ee6fad335618cbe85ad9e11e.zip
linuxkpi: Add `io_mapping_map_user()` and `remap_pfn_range()`
The code comes from the i915 DRM driver. In Linux commits b739f125e4ebd73d10ed30a856574e13649119ed and b12d691ea5e01db42ccf3b4207e57cb3ce7cfe91 (Linux 5.13), the i915 DRM driver dropped specific implementations to use Linux generic functions. Therefore I moved the FreeBSD code from that i915 driver to linuxkpi. However, these commits were later reverted (also in Linux 5.13) so the i915 driver doesn't use these functions. But perhaps it will help in the future. To sum up, the code comes from the i915 DRM driver but it doesn't use it (i.e. it continues to use its internal implementation). Reviewed by: manu Approved by: manu Differential Revision: https://reviews.freebsd.org/D38088
Diffstat (limited to 'sys/compat/linuxkpi/common/src')
-rw-r--r--sys/compat/linuxkpi/common/src/linux_page.c58
1 files changed, 58 insertions, 0 deletions
diff --git a/sys/compat/linuxkpi/common/src/linux_page.c b/sys/compat/linuxkpi/common/src/linux_page.c
index 4c93d2f69354..c4d0d332af14 100644
--- a/sys/compat/linuxkpi/common/src/linux_page.c
+++ b/sys/compat/linuxkpi/common/src/linux_page.c
@@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$");
#include <linux/kernel.h>
#include <linux/idr.h>
#include <linux/io.h>
+#include <linux/io-mapping.h>
#ifdef __i386__
DEFINE_IDR(mtrr_idr);
@@ -334,6 +335,63 @@ retry:
return (VM_FAULT_NOPAGE);
}
+int
+lkpi_remap_pfn_range(struct vm_area_struct *vma, unsigned long start_addr,
+ unsigned long start_pfn, unsigned long size, pgprot_t prot)
+{
+ vm_object_t vm_obj;
+ unsigned long addr, pfn;
+ int err = 0;
+
+ vm_obj = vma->vm_obj;
+
+ VM_OBJECT_WLOCK(vm_obj);
+ for (addr = start_addr, pfn = start_pfn;
+ addr < start_addr + size;
+ addr += PAGE_SIZE) {
+ vm_fault_t ret;
+retry:
+ ret = lkpi_vmf_insert_pfn_prot_locked(vma, addr, pfn, prot);
+
+ if ((ret & VM_FAULT_OOM) != 0) {
+ VM_OBJECT_WUNLOCK(vm_obj);
+ vm_wait(NULL);
+ VM_OBJECT_WLOCK(vm_obj);
+ goto retry;
+ }
+
+ if ((ret & VM_FAULT_ERROR) != 0) {
+ err = -EFAULT;
+ break;
+ }
+
+ pfn++;
+ }
+ VM_OBJECT_WUNLOCK(vm_obj);
+
+ if (unlikely(err)) {
+ zap_vma_ptes(vma, start_addr,
+ (pfn - start_pfn) << PAGE_SHIFT);
+ return (err);
+ }
+
+ return (0);
+}
+
+int
+lkpi_io_mapping_map_user(struct io_mapping *iomap,
+ struct vm_area_struct *vma, unsigned long addr,
+ unsigned long pfn, unsigned long size)
+{
+ pgprot_t prot;
+ int ret;
+
+ prot = cachemode2protval(iomap->attr);
+ ret = lkpi_remap_pfn_range(vma, addr, pfn, size, prot);
+
+ return (ret);
+}
+
/*
* Although FreeBSD version of unmap_mapping_range has semantics and types of
* parameters compatible with Linux version, the values passed in are different