|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * KUnit userspace memory allocation resource management. | 
|  | */ | 
|  | #include <kunit/resource.h> | 
|  | #include <kunit/test.h> | 
|  | #include <linux/kthread.h> | 
|  | #include <linux/mm.h> | 
|  |  | 
|  | struct kunit_vm_mmap_resource { | 
|  | unsigned long addr; | 
|  | size_t size; | 
|  | }; | 
|  |  | 
|  | /* vm_mmap() arguments */ | 
|  | struct kunit_vm_mmap_params { | 
|  | struct file *file; | 
|  | unsigned long addr; | 
|  | unsigned long len; | 
|  | unsigned long prot; | 
|  | unsigned long flag; | 
|  | unsigned long offset; | 
|  | }; | 
|  |  | 
|  | int kunit_attach_mm(void) | 
|  | { | 
|  | struct mm_struct *mm; | 
|  |  | 
|  | if (current->mm) | 
|  | return 0; | 
|  |  | 
|  | /* arch_pick_mmap_layout() is only sane with MMU systems. */ | 
|  | if (!IS_ENABLED(CONFIG_MMU)) | 
|  | return -EINVAL; | 
|  |  | 
|  | mm = mm_alloc(); | 
|  | if (!mm) | 
|  | return -ENOMEM; | 
|  |  | 
|  | /* Define the task size. */ | 
|  | mm->task_size = TASK_SIZE; | 
|  |  | 
|  | /* Make sure we can allocate new VMAs. */ | 
|  | arch_pick_mmap_layout(mm, ¤t->signal->rlim[RLIMIT_STACK]); | 
|  |  | 
|  | /* Attach the mm. It will be cleaned up when the process dies. */ | 
|  | kthread_use_mm(mm); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(kunit_attach_mm); | 
|  |  | 
|  | static int kunit_vm_mmap_init(struct kunit_resource *res, void *context) | 
|  | { | 
|  | struct kunit_vm_mmap_params *p = context; | 
|  | struct kunit_vm_mmap_resource vres; | 
|  | int ret; | 
|  |  | 
|  | ret = kunit_attach_mm(); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | vres.size = p->len; | 
|  | vres.addr = vm_mmap(p->file, p->addr, p->len, p->prot, p->flag, p->offset); | 
|  | if (!vres.addr) | 
|  | return -ENOMEM; | 
|  | res->data = kmemdup(&vres, sizeof(vres), GFP_KERNEL); | 
|  | if (!res->data) { | 
|  | vm_munmap(vres.addr, vres.size); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void kunit_vm_mmap_free(struct kunit_resource *res) | 
|  | { | 
|  | struct kunit_vm_mmap_resource *vres = res->data; | 
|  |  | 
|  | /* | 
|  | * Since this is executed from the test monitoring process, | 
|  | * the test's mm has already been torn down. We don't need | 
|  | * to run vm_munmap(vres->addr, vres->size), only clean up | 
|  | * the vres. | 
|  | */ | 
|  |  | 
|  | kfree(vres); | 
|  | res->data = NULL; | 
|  | } | 
|  |  | 
|  | unsigned long kunit_vm_mmap(struct kunit *test, struct file *file, | 
|  | unsigned long addr, unsigned long len, | 
|  | unsigned long prot, unsigned long flag, | 
|  | unsigned long offset) | 
|  | { | 
|  | struct kunit_vm_mmap_params params = { | 
|  | .file = file, | 
|  | .addr = addr, | 
|  | .len = len, | 
|  | .prot = prot, | 
|  | .flag = flag, | 
|  | .offset = offset, | 
|  | }; | 
|  | struct kunit_vm_mmap_resource *vres; | 
|  |  | 
|  | vres = kunit_alloc_resource(test, | 
|  | kunit_vm_mmap_init, | 
|  | kunit_vm_mmap_free, | 
|  | GFP_KERNEL, | 
|  | ¶ms); | 
|  | if (vres) | 
|  | return vres->addr; | 
|  | return 0; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(kunit_vm_mmap); | 
|  |  | 
|  | MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); |