summaryrefslogtreecommitdiffstats
path: root/arch/x86_64/ia32
diff options
context:
space:
mode:
authorAndi Kleen <ak@muc.de>2005-04-16 15:24:55 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:24:55 -0700
commit1e01441051dda3bb01c455b6e20bce6d00563d82 (patch)
tree5dc4c69dd4522ca569f70ead0ecbb923f1451891 /arch/x86_64/ia32
parent35faa71484287fc150b8498cd5acae59ad17a356 (diff)
downloadop-kernel-dev-1e01441051dda3bb01c455b6e20bce6d00563d82.zip
op-kernel-dev-1e01441051dda3bb01c455b6e20bce6d00563d82.tar.gz
[PATCH] x86_64: Use a VMA for the 32bit vsyscall
Use a real VMA to map the 32bit vsyscall page This interacts better with Hugh's upcomming VMA walk optimization Also removes some ugly special cases. Code roughly modelled after the ppc64 vdso version from Ben Herrenschmidt. Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/x86_64/ia32')
-rw-r--r--arch/x86_64/ia32/ia32_binfmt.c4
-rw-r--r--arch/x86_64/ia32/syscall32.c92
2 files changed, 53 insertions, 43 deletions
diff --git a/arch/x86_64/ia32/ia32_binfmt.c b/arch/x86_64/ia32/ia32_binfmt.c
index 93d568d..99b5220 100644
--- a/arch/x86_64/ia32/ia32_binfmt.c
+++ b/arch/x86_64/ia32/ia32_binfmt.c
@@ -312,6 +312,10 @@ MODULE_AUTHOR("Eric Youngdale, Andi Kleen");
static void elf32_init(struct pt_regs *);
+#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
+#define arch_setup_additional_pages syscall32_setup_pages
+extern int syscall32_setup_pages(struct linux_binprm *, int exstack);
+
#include "../../../fs/binfmt_elf.c"
static void elf32_init(struct pt_regs *regs)
diff --git a/arch/x86_64/ia32/syscall32.c b/arch/x86_64/ia32/syscall32.c
index 399ff49..01d8db1 100644
--- a/arch/x86_64/ia32/syscall32.c
+++ b/arch/x86_64/ia32/syscall32.c
@@ -9,6 +9,7 @@
#include <linux/gfp.h>
#include <linux/init.h>
#include <linux/stringify.h>
+#include <linux/security.h>
#include <asm/proto.h>
#include <asm/tlbflush.h>
#include <asm/ia32_unistd.h>
@@ -30,51 +31,57 @@ extern int sysctl_vsyscall32;
char *syscall32_page;
static int use_sysenter = -1;
-/*
- * Map the 32bit vsyscall page on demand.
- *
- * RED-PEN: This knows too much about high level VM.
- *
- * Alternative would be to generate a vma with appropriate backing options
- * and let it be handled by generic VM.
- */
-int __map_syscall32(struct mm_struct *mm, unsigned long address)
-{
- pgd_t *pgd;
- pud_t *pud;
- pte_t *pte;
- pmd_t *pmd;
- int err = -ENOMEM;
-
- spin_lock(&mm->page_table_lock);
- pgd = pgd_offset(mm, address);
- pud = pud_alloc(mm, pgd, address);
- if (pud) {
- pmd = pmd_alloc(mm, pud, address);
- if (pmd && (pte = pte_alloc_map(mm, pmd, address)) != NULL) {
- if (pte_none(*pte)) {
- set_pte(pte,
- mk_pte(virt_to_page(syscall32_page),
- PAGE_KERNEL_VSYSCALL32));
- }
- /* Flush only the local CPU. Other CPUs taking a fault
- will just end up here again
- This probably not needed and just paranoia. */
- __flush_tlb_one(address);
- err = 0;
- }
- }
- spin_unlock(&mm->page_table_lock);
- return err;
+static struct page *
+syscall32_nopage(struct vm_area_struct *vma, unsigned long adr, int *type)
+{
+ struct page *p = virt_to_page(adr - vma->vm_start + syscall32_page);
+ get_page(p);
+ return p;
}
-int map_syscall32(struct mm_struct *mm, unsigned long address)
+/* Prevent VMA merging */
+static void syscall32_vma_close(struct vm_area_struct *vma)
{
- int err;
- down_read(&mm->mmap_sem);
- err = __map_syscall32(mm, address);
- up_read(&mm->mmap_sem);
- return err;
+}
+
+static struct vm_operations_struct syscall32_vm_ops = {
+ .close = syscall32_vma_close,
+ .nopage = syscall32_nopage,
+};
+
+struct linux_binprm;
+
+/* Setup a VMA at program startup for the vsyscall page */
+int syscall32_setup_pages(struct linux_binprm *bprm, int exstack)
+{
+ int npages = (VSYSCALL32_END - VSYSCALL32_BASE) >> PAGE_SHIFT;
+ struct vm_area_struct *vma;
+ struct mm_struct *mm = current->mm;
+
+ vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+ if (!vma)
+ return -ENOMEM;
+ if (security_vm_enough_memory(npages)) {
+ kmem_cache_free(vm_area_cachep, vma);
+ return -ENOMEM;
+ }
+
+ memset(vma, 0, sizeof(struct vm_area_struct));
+ /* Could randomize here */
+ vma->vm_start = VSYSCALL32_BASE;
+ vma->vm_end = VSYSCALL32_END;
+ /* MAYWRITE to allow gdb to COW and set breakpoints */
+ vma->vm_flags = VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYEXEC|VM_MAYEXEC|VM_MAYWRITE;
+ vma->vm_flags |= mm->def_flags;
+ vma->vm_page_prot = protection_map[vma->vm_flags & 7];
+ vma->vm_ops = &syscall32_vm_ops;
+ vma->vm_mm = mm;
+
+ down_write(&mm->mmap_sem);
+ insert_vm_struct(mm, vma);
+ mm->total_vm += npages;
+ up_write(&mm->mmap_sem);
+ return 0;
}
static int __init init_syscall32(void)
@@ -82,7 +89,6 @@ static int __init init_syscall32(void)
syscall32_page = (void *)get_zeroed_page(GFP_KERNEL);
if (!syscall32_page)
panic("Cannot allocate syscall32 page");
- SetPageReserved(virt_to_page(syscall32_page));
if (use_sysenter > 0) {
memcpy(syscall32_page, syscall32_sysenter,
syscall32_sysenter_end - syscall32_sysenter);
OpenPOWER on IntegriCloud