From: Lijun Fang fanglijun3@huawei.com
ascend inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I4JMM0 CVE: NA
-------------------------------------------------
Add alloc and release memory functions in svm. And the physical address of the memory is within 4GB.
For example: /* alloc */ fd = open("dev/svm0",); mmap(0, ALLOC_SIZE,, MAP_PA32BIT, fd, 0);
/* free */ ioctl(fd, SVM_IOCTL_RELEASE_PHYS32,); close(fd);
Signed-off-by: Lijun Fang fanglijun3@huawei.com Reviewed-by: Weilong Chen chenweilong@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- arch/alpha/include/uapi/asm/mman.h | 1 + arch/mips/include/uapi/asm/mman.h | 1 + arch/parisc/include/uapi/asm/mman.h | 1 + arch/powerpc/include/uapi/asm/mman.h | 1 + arch/sparc/include/uapi/asm/mman.h | 1 + arch/xtensa/include/uapi/asm/mman.h | 1 + drivers/char/svm.c | 239 +++++++++++++++++++++++++++ include/linux/mm.h | 1 + include/uapi/asm-generic/mman.h | 1 + mm/mmap.c | 4 + 10 files changed, 251 insertions(+)
diff --git a/arch/alpha/include/uapi/asm/mman.h b/arch/alpha/include/uapi/asm/mman.h index a18ec7f63888..87abc7b03360 100644 --- a/arch/alpha/include/uapi/asm/mman.h +++ b/arch/alpha/include/uapi/asm/mman.h @@ -31,6 +31,7 @@ #define MAP_STACK 0x80000 /* give out an address that is best suited for process/thread stacks */ #define MAP_HUGETLB 0x100000 /* create a huge page mapping */ #define MAP_FIXED_NOREPLACE 0x200000/* MAP_FIXED which doesn't unmap underlying mapping */ +#define MAP_PA32BIT 0x400000 /* physical address is within 4G */
#define MS_ASYNC 1 /* sync memory asynchronously */ #define MS_SYNC 2 /* synchronous memory sync */ diff --git a/arch/mips/include/uapi/asm/mman.h b/arch/mips/include/uapi/asm/mman.h index 57dc2ac4f8bd..61cd225fcaa4 100644 --- a/arch/mips/include/uapi/asm/mman.h +++ b/arch/mips/include/uapi/asm/mman.h @@ -49,6 +49,7 @@ #define MAP_STACK 0x40000 /* give out an address that is best suited for process/thread stacks */ #define MAP_HUGETLB 0x80000 /* create a huge page mapping */ #define MAP_FIXED_NOREPLACE 0x100000 /* MAP_FIXED which doesn't unmap underlying mapping */ +#define MAP_PA32BIT 0x400000 /* physical address is within 4G */
/* * Flags for msync diff --git a/arch/parisc/include/uapi/asm/mman.h b/arch/parisc/include/uapi/asm/mman.h index ab78cba446ed..851678907640 100644 --- a/arch/parisc/include/uapi/asm/mman.h +++ b/arch/parisc/include/uapi/asm/mman.h @@ -26,6 +26,7 @@ #define MAP_HUGETLB 0x80000 /* create a huge page mapping */ #define MAP_FIXED_NOREPLACE 0x100000 /* MAP_FIXED which doesn't unmap underlying mapping */ #define MAP_UNINITIALIZED 0 /* uninitialized anonymous mmap */ +#define MAP_PA32BIT 0x400000 /* physical address is within 4G */
#define MS_SYNC 1 /* synchronous memory sync */ #define MS_ASYNC 2 /* sync memory asynchronously */ diff --git a/arch/powerpc/include/uapi/asm/mman.h b/arch/powerpc/include/uapi/asm/mman.h index c0c737215b00..f0eb04780148 100644 --- a/arch/powerpc/include/uapi/asm/mman.h +++ b/arch/powerpc/include/uapi/asm/mman.h @@ -25,6 +25,7 @@ #define MCL_CURRENT 0x2000 /* lock all currently mapped pages */ #define MCL_FUTURE 0x4000 /* lock all additions to address space */ #define MCL_ONFAULT 0x8000 /* lock all pages that are faulted in */ +#define MAP_PA32BIT 0x400000 /* physical address is within 4G */
/* Override any generic PKEY permission defines */ #define PKEY_DISABLE_EXECUTE 0x4 diff --git a/arch/sparc/include/uapi/asm/mman.h b/arch/sparc/include/uapi/asm/mman.h index cec9f4109687..8caf19c604d0 100644 --- a/arch/sparc/include/uapi/asm/mman.h +++ b/arch/sparc/include/uapi/asm/mman.h @@ -21,5 +21,6 @@ #define MCL_CURRENT 0x2000 /* lock all currently mapped pages */ #define MCL_FUTURE 0x4000 /* lock all additions to address space */ #define MCL_ONFAULT 0x8000 /* lock all pages that are faulted in */ +#define MAP_PA32BIT 0x400000 /* physical address is within 4G */
#endif /* _UAPI__SPARC_MMAN_H__ */ diff --git a/arch/xtensa/include/uapi/asm/mman.h b/arch/xtensa/include/uapi/asm/mman.h index e5e643752947..a52ac8462b7d 100644 --- a/arch/xtensa/include/uapi/asm/mman.h +++ b/arch/xtensa/include/uapi/asm/mman.h @@ -56,6 +56,7 @@ #define MAP_STACK 0x40000 /* give out an address that is best suited for process/thread stacks */ #define MAP_HUGETLB 0x80000 /* create a huge page mapping */ #define MAP_FIXED_NOREPLACE 0x100000 /* MAP_FIXED which doesn't unmap underlying mapping */ +#define MAP_PA32BIT 0x400000 /* physical address is within 4G */ #define MAP_UNINITIALIZED 0x4000000 /* For anonymous mmap, memory could be * uninitialized */
diff --git a/drivers/char/svm.c b/drivers/char/svm.c index a430062f76e4..b1dd373a745c 100644 --- a/drivers/char/svm.c +++ b/drivers/char/svm.c @@ -39,6 +39,10 @@ #define SVM_IOCTL_PROCESS_BIND 0xffff
#define CORE_SID 0 + +#define SVM_IOCTL_RELEASE_PHYS32 0xfff3 +#define MMAP_PHY32_MAX (16 * 1024 * 1024) + static int probe_index; static LIST_HEAD(child_list); static DECLARE_RWSEM(svm_sem); @@ -96,6 +100,8 @@ static char *svm_cmd_to_string(unsigned int cmd) switch (cmd) { case SVM_IOCTL_PROCESS_BIND: return "bind"; + case SVM_IOCTL_RELEASE_PHYS32: + return "release phys"; default: return "unsupported"; } @@ -402,6 +408,83 @@ static int svm_process_bind(struct task_struct *task, return err; }
+static pte_t *svm_get_pte(struct vm_area_struct *vma, + pud_t *pud, + unsigned long addr, + unsigned long *page_size, + unsigned long *offset) +{ + pte_t *pte = NULL; + unsigned long size = 0; + + if (is_vm_hugetlb_page(vma)) { + if (pud_present(*pud)) { + if (pud_huge(*pud)) { + pte = (pte_t *)pud; + *offset = addr & (PUD_SIZE - 1); + size = PUD_SIZE; + } else { + pte = (pte_t *)pmd_offset(pud, addr); + *offset = addr & (PMD_SIZE - 1); + size = PMD_SIZE; + } + } else { + pr_err("%s:hugetlb but pud not present\n", __func__); + } + } else { + pmd_t *pmd = pmd_offset(pud, addr); + + if (pmd_none(*pmd)) + return NULL; + + if (pmd_trans_huge(*pmd)) { + pte = (pte_t *)pmd; + *offset = addr & (PMD_SIZE - 1); + size = PMD_SIZE; + } else if (pmd_trans_unstable(pmd)) { + pr_warn("%s: thp unstable\n", __func__); + } else { + pte = pte_offset_map(pmd, addr); + *offset = addr & (PAGE_SIZE - 1); + size = PAGE_SIZE; + } + } + + if (page_size) + *page_size = size; + + return pte; +} + +/* Must be called with mmap_lock held */ +static pte_t *svm_walk_pt(unsigned long addr, unsigned long *page_size, + unsigned long *offset) +{ + pgd_t *pgd = NULL; + p4d_t *p4d = NULL; + pud_t *pud = NULL; + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma = NULL; + + vma = find_vma(mm, addr); + if (!vma) + return NULL; + + pgd = pgd_offset(mm, addr); + if (pgd_none_or_clear_bad(pgd)) + return NULL; + + p4d = p4d_offset(pgd, addr); + if (p4d_none_or_clear_bad(p4d)) + return NULL; + + pud = pud_offset(p4d, addr); + if (pud_none_or_clear_bad(pud)) + return NULL; + + return svm_get_pte(vma, pud, addr, page_size, offset); +} + static struct bus_type svm_bus_type = { .name = "svm_bus", }; @@ -411,6 +494,157 @@ static int svm_open(struct inode *inode, struct file *file) return 0; }
+static unsigned long svm_get_unmapped_area(struct file *file, + unsigned long addr0, unsigned long len, + unsigned long pgoff, unsigned long flags) +{ + unsigned long addr = addr0; + struct mm_struct *mm = current->mm; + struct vm_unmapped_area_info info; + struct svm_device *sdev = file_to_sdev(file); + + if (!acpi_disabled) + return -EPERM; + + if (flags & MAP_FIXED) { + if (IS_ALIGNED(addr, len)) + return addr; + + dev_err(sdev->dev, "MAP_FIXED but not aligned\n"); + return -EINVAL; //lint !e570 + } + + if (addr) { + struct vm_area_struct *vma = NULL; + + addr = ALIGN(addr, len); + + vma = find_vma(mm, addr); + if (TASK_SIZE - len >= addr && addr >= mmap_min_addr && + (vma == NULL || addr + len <= vm_start_gap(vma))) + return addr; + } + + info.flags = VM_UNMAPPED_AREA_TOPDOWN; + info.length = len; + info.low_limit = max(PAGE_SIZE, mmap_min_addr); + info.high_limit = mm->mmap_base; + info.align_mask = ((len >> PAGE_SHIFT) - 1) << PAGE_SHIFT; + info.align_offset = pgoff << PAGE_SHIFT; + + addr = vm_unmapped_area(&info); + + if (offset_in_page(addr)) { + VM_BUG_ON(addr != -ENOMEM); + info.flags = 0; + info.low_limit = TASK_UNMAPPED_BASE; + info.high_limit = TASK_SIZE; + + addr = vm_unmapped_area(&info); + } + + return addr; +} + +static int svm_mmap(struct file *file, struct vm_area_struct *vma) +{ + int err; + struct svm_device *sdev = file_to_sdev(file); + + if (!acpi_disabled) + return -EPERM; + + if (vma->vm_flags & VM_PA32BIT) { + unsigned long vm_size = vma->vm_end - vma->vm_start; + struct page *page = NULL; + + if ((vma->vm_end < vma->vm_start) || (vm_size > MMAP_PHY32_MAX)) + return -EINVAL; + + /* vma->vm_pgoff transfer the nid */ + if (vma->vm_pgoff == 0) + page = alloc_pages(GFP_KERNEL | GFP_DMA32, + get_order(vm_size)); + else + page = alloc_pages_node((int)vma->vm_pgoff, + GFP_KERNEL | __GFP_THISNODE, + get_order(vm_size)); + if (!page) { + dev_err(sdev->dev, "fail to alloc page on node 0x%lx\n", + vma->vm_pgoff); + return -ENOMEM; + } + + err = remap_pfn_range(vma, + vma->vm_start, + page_to_pfn(page), + vm_size, vma->vm_page_prot); + if (err) + dev_err(sdev->dev, + "fail to remap 0x%pK err=%d\n", + (void *)vma->vm_start, err); + } else { + if ((vma->vm_end < vma->vm_start) || + ((vma->vm_end - vma->vm_start) > sdev->l2size)) + return -EINVAL; + + vma->vm_page_prot = __pgprot((~PTE_SHARED) & + vma->vm_page_prot.pgprot); + + err = remap_pfn_range(vma, + vma->vm_start, + sdev->l2buff >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + __pgprot(vma->vm_page_prot.pgprot | PTE_DIRTY)); + if (err) + dev_err(sdev->dev, + "fail to remap 0x%pK err=%d\n", + (void *)vma->vm_start, err); + } + + return err; +} + +static int svm_release_phys32(unsigned long __user *arg) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma = NULL; + struct page *page = NULL; + pte_t *pte = NULL; + unsigned long phys, addr, offset; + unsigned int len = 0; + + if (arg == NULL) + return -EINVAL; + + if (get_user(addr, arg)) + return -EFAULT; + + down_read(&mm->mmap_lock); + pte = svm_walk_pt(addr, NULL, &offset); + if (pte && pte_present(*pte)) { + phys = PFN_PHYS(pte_pfn(*pte)) + offset; + } else { + up_read(&mm->mmap_lock); + return -EINVAL; + } + + vma = find_vma(mm, addr); + if (!vma) { + up_read(&mm->mmap_lock); + return -EFAULT; + } + + page = phys_to_page(phys); + len = vma->vm_end - vma->vm_start; + + __free_pages(page, get_order(len)); + + up_read(&mm->mmap_lock); + + return 0; +} + static long svm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -455,6 +689,9 @@ static long svm_ioctl(struct file *file, unsigned int cmd, return -EFAULT; } break; + case SVM_IOCTL_RELEASE_PHYS32: + err = svm_release_phys32((unsigned long __user *)arg); + break; default: err = -EINVAL; } @@ -469,6 +706,8 @@ static long svm_ioctl(struct file *file, unsigned int cmd, static const struct file_operations svm_fops = { .owner = THIS_MODULE, .open = svm_open, + .mmap = svm_mmap, + .get_unmapped_area = svm_get_unmapped_area, .unlocked_ioctl = svm_ioctl, };
diff --git a/include/linux/mm.h b/include/linux/mm.h index 3780281c8112..ae9b6688677f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -298,6 +298,7 @@ extern unsigned int kobjsize(const void *objp); #define VM_HUGEPAGE 0x20000000 /* MADV_HUGEPAGE marked this vma */ #define VM_NOHUGEPAGE 0x40000000 /* MADV_NOHUGEPAGE marked this vma */ #define VM_MERGEABLE 0x80000000 /* KSM may merge identical pages */ +#define VM_PA32BIT 0x400000000 /* Physical address is within 4G */
#ifdef CONFIG_COHERENT_DEVICE #define VM_CDM 0x100000000 /* Contains coherent device memory */ diff --git a/include/uapi/asm-generic/mman.h b/include/uapi/asm-generic/mman.h index 57e8195d0b53..344bb9b090a7 100644 --- a/include/uapi/asm-generic/mman.h +++ b/include/uapi/asm-generic/mman.h @@ -9,6 +9,7 @@ #define MAP_EXECUTABLE 0x1000 /* mark it as an executable */ #define MAP_LOCKED 0x2000 /* pages are locked */ #define MAP_NORESERVE 0x4000 /* don't check for reservations */ +#define MAP_PA32BIT 0x400000 /* physical address is within 4G */
/* * Bits [26:31] are reserved, see asm-generic/hugetlb_encode.h diff --git a/mm/mmap.c b/mm/mmap.c index f63925a21c95..f705137fd248 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1462,6 +1462,10 @@ __do_mmap(struct file *file, unsigned long addr, unsigned long len, pkey = 0; }
+ /* Physical address is within 4G */ + if (flags & MAP_PA32BIT) + vm_flags |= VM_PA32BIT; + /* Do simple checking here so the lower-level routines won't have * to. we assume access permissions have been handled by the open * of the memory object, so we don't do any here.