vdso.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Author: Huacai Chen <[email protected]>
  4. * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
  5. */
  6. #include <linux/binfmts.h>
  7. #include <linux/elf.h>
  8. #include <linux/err.h>
  9. #include <linux/init.h>
  10. #include <linux/ioport.h>
  11. #include <linux/kernel.h>
  12. #include <linux/mm.h>
  13. #include <linux/random.h>
  14. #include <linux/sched.h>
  15. #include <linux/slab.h>
  16. #include <linux/timekeeper_internal.h>
  17. #include <asm/page.h>
  18. #include <asm/vdso.h>
  19. #include <vdso/helpers.h>
  20. #include <vdso/vsyscall.h>
  21. #include <generated/vdso-offsets.h>
  22. extern char vdso_start[], vdso_end[];
  23. /* Kernel-provided data used by the VDSO. */
  24. static union {
  25. u8 page[VDSO_DATA_SIZE];
  26. struct loongarch_vdso_data vdata;
  27. } loongarch_vdso_data __page_aligned_data;
  28. static struct page *vdso_pages[] = { NULL };
  29. struct vdso_data *vdso_data = loongarch_vdso_data.vdata.data;
  30. struct vdso_pcpu_data *vdso_pdata = loongarch_vdso_data.vdata.pdata;
  31. static int vdso_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma)
  32. {
  33. current->mm->context.vdso = (void *)(new_vma->vm_start);
  34. return 0;
  35. }
  36. struct loongarch_vdso_info vdso_info = {
  37. .vdso = vdso_start,
  38. .size = PAGE_SIZE,
  39. .code_mapping = {
  40. .name = "[vdso]",
  41. .pages = vdso_pages,
  42. .mremap = vdso_mremap,
  43. },
  44. .data_mapping = {
  45. .name = "[vvar]",
  46. },
  47. .offset_sigreturn = vdso_offset_sigreturn,
  48. };
  49. static int __init init_vdso(void)
  50. {
  51. unsigned long i, cpu, pfn;
  52. BUG_ON(!PAGE_ALIGNED(vdso_info.vdso));
  53. BUG_ON(!PAGE_ALIGNED(vdso_info.size));
  54. for_each_possible_cpu(cpu)
  55. vdso_pdata[cpu].node = cpu_to_node(cpu);
  56. pfn = __phys_to_pfn(__pa_symbol(vdso_info.vdso));
  57. for (i = 0; i < vdso_info.size / PAGE_SIZE; i++)
  58. vdso_info.code_mapping.pages[i] = pfn_to_page(pfn + i);
  59. return 0;
  60. }
  61. subsys_initcall(init_vdso);
  62. static unsigned long vdso_base(void)
  63. {
  64. unsigned long base = STACK_TOP;
  65. if (current->flags & PF_RANDOMIZE) {
  66. base += prandom_u32_max(VDSO_RANDOMIZE_SIZE);
  67. base = PAGE_ALIGN(base);
  68. }
  69. return base;
  70. }
  71. int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
  72. {
  73. int ret;
  74. unsigned long vvar_size, size, data_addr, vdso_addr;
  75. struct mm_struct *mm = current->mm;
  76. struct vm_area_struct *vma;
  77. struct loongarch_vdso_info *info = current->thread.vdso;
  78. if (mmap_write_lock_killable(mm))
  79. return -EINTR;
  80. /*
  81. * Determine total area size. This includes the VDSO data itself
  82. * and the data pages.
  83. */
  84. vvar_size = VDSO_DATA_SIZE;
  85. size = vvar_size + info->size;
  86. data_addr = get_unmapped_area(NULL, vdso_base(), size, 0, 0);
  87. if (IS_ERR_VALUE(data_addr)) {
  88. ret = data_addr;
  89. goto out;
  90. }
  91. vdso_addr = data_addr + VDSO_DATA_SIZE;
  92. vma = _install_special_mapping(mm, data_addr, vvar_size,
  93. VM_READ | VM_MAYREAD,
  94. &info->data_mapping);
  95. if (IS_ERR(vma)) {
  96. ret = PTR_ERR(vma);
  97. goto out;
  98. }
  99. /* Map VDSO data page. */
  100. ret = remap_pfn_range(vma, data_addr,
  101. virt_to_phys(&loongarch_vdso_data) >> PAGE_SHIFT,
  102. vvar_size, PAGE_READONLY);
  103. if (ret)
  104. goto out;
  105. /* Map VDSO code page. */
  106. vma = _install_special_mapping(mm, vdso_addr, info->size,
  107. VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
  108. &info->code_mapping);
  109. if (IS_ERR(vma)) {
  110. ret = PTR_ERR(vma);
  111. goto out;
  112. }
  113. mm->context.vdso = (void *)vdso_addr;
  114. ret = 0;
  115. out:
  116. mmap_write_unlock(mm);
  117. return ret;
  118. }