diff options
Diffstat (limited to 'arch/um/kernel/tlb.c')
-rw-r--r-- | arch/um/kernel/tlb.c | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/arch/um/kernel/tlb.c b/arch/um/kernel/tlb.c new file mode 100644 index 0000000..eda477e --- /dev/null +++ b/arch/um/kernel/tlb.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) + * Licensed under the GPL + */ + +#include "linux/mm.h" +#include "asm/page.h" +#include "asm/pgalloc.h" +#include "asm/tlbflush.h" +#include "choose-mode.h" +#include "mode_kern.h" +#include "user_util.h" +#include "tlb.h" +#include "mem.h" +#include "mem_user.h" +#include "os.h" + +#define ADD_ROUND(n, inc) (((n) + (inc)) & ~((inc) - 1)) + +void fix_range_common(struct mm_struct *mm, unsigned long start_addr, + unsigned long end_addr, int force, int data, + void (*do_ops)(int, struct host_vm_op *, int)) +{ + pgd_t *npgd; + pud_t *npud; + pmd_t *npmd; + pte_t *npte; + unsigned long addr, end; + int r, w, x; + struct host_vm_op ops[16]; + int op_index = -1, last_op = sizeof(ops) / sizeof(ops[0]) - 1; + + if(mm == NULL) return; + + for(addr = start_addr; addr < end_addr;){ + npgd = pgd_offset(mm, addr); + if(!pgd_present(*npgd)){ + end = ADD_ROUND(addr, PGDIR_SIZE); + if(end > end_addr) + end = end_addr; + if(force || pgd_newpage(*npgd)){ + op_index = add_munmap(addr, end - addr, ops, + op_index, last_op, data, + do_ops); + pgd_mkuptodate(*npgd); + } + addr = end; + continue; + } + + npud = pud_offset(npgd, addr); + if(!pud_present(*npud)){ + end = ADD_ROUND(addr, PUD_SIZE); + if(end > end_addr) + end = end_addr; + if(force || pud_newpage(*npud)){ + op_index = add_munmap(addr, end - addr, ops, + op_index, last_op, data, + do_ops); + pud_mkuptodate(*npud); + } + addr = end; + continue; + } + + npmd = pmd_offset(npud, addr); + if(!pmd_present(*npmd)){ + end = ADD_ROUND(addr, PMD_SIZE); + if(end > end_addr) + end = end_addr; + if(force || pmd_newpage(*npmd)){ + op_index = add_munmap(addr, end - addr, ops, + op_index, last_op, data, + do_ops); + pmd_mkuptodate(*npmd); + } + addr = end; + continue; + } + + npte = pte_offset_kernel(npmd, addr); + r = pte_read(*npte); + w = pte_write(*npte); + x = pte_exec(*npte); + if(!pte_dirty(*npte)) + w = 0; + if(!pte_young(*npte)){ + r = 0; + w = 0; + } + if(force || pte_newpage(*npte)){ + if(pte_present(*npte)) + op_index = add_mmap(addr, + pte_val(*npte) & PAGE_MASK, + PAGE_SIZE, r, w, x, ops, + op_index, last_op, data, + do_ops); + else op_index = add_munmap(addr, PAGE_SIZE, ops, + op_index, last_op, data, + do_ops); + } + else if(pte_newprot(*npte)) + op_index = add_mprotect(addr, PAGE_SIZE, r, w, x, ops, + op_index, last_op, data, + do_ops); + + *npte = pte_mkuptodate(*npte); + addr += PAGE_SIZE; + } + (*do_ops)(data, ops, op_index); +} + +int flush_tlb_kernel_range_common(unsigned long start, unsigned long end) +{ + struct mm_struct *mm; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + unsigned long addr, last; + int updated = 0, err; + + mm = &init_mm; + for(addr = start; addr < end;){ + pgd = pgd_offset(mm, addr); + if(!pgd_present(*pgd)){ + last = ADD_ROUND(addr, PGDIR_SIZE); + if(last > end) + last = end; + if(pgd_newpage(*pgd)){ + updated = 1; + err = os_unmap_memory((void *) addr, + last - addr); + if(err < 0) + panic("munmap failed, errno = %d\n", + -err); + } + addr = last; + continue; + } + + pud = pud_offset(pgd, addr); + if(!pud_present(*pud)){ + last = ADD_ROUND(addr, PUD_SIZE); + if(last > end) + last = end; + if(pud_newpage(*pud)){ + updated = 1; + err = os_unmap_memory((void *) addr, + last - addr); + if(err < 0) + panic("munmap failed, errno = %d\n", + -err); + } + addr = last; + continue; + } + + pmd = pmd_offset(pud, addr); + if(!pmd_present(*pmd)){ + last = ADD_ROUND(addr, PMD_SIZE); + if(last > end) + last = end; + if(pmd_newpage(*pmd)){ + updated = 1; + err = os_unmap_memory((void *) addr, + last - addr); + if(err < 0) + panic("munmap failed, errno = %d\n", + -err); + } + addr = last; + continue; + } + + pte = pte_offset_kernel(pmd, addr); + if(!pte_present(*pte) || pte_newpage(*pte)){ + updated = 1; + err = os_unmap_memory((void *) addr, + PAGE_SIZE); + if(err < 0) + panic("munmap failed, errno = %d\n", + -err); + if(pte_present(*pte)) + map_memory(addr, + pte_val(*pte) & PAGE_MASK, + PAGE_SIZE, 1, 1, 1); + } + else if(pte_newprot(*pte)){ + updated = 1; + protect_memory(addr, PAGE_SIZE, 1, 1, 1, 1); + } + addr += PAGE_SIZE; + } + return(updated); +} + +void flush_tlb_page(struct vm_area_struct *vma, unsigned long address) +{ + address &= PAGE_MASK; + flush_tlb_range(vma, address, address + PAGE_SIZE); +} + +void flush_tlb_all(void) +{ + flush_tlb_mm(current->mm); +} + +void flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ + CHOOSE_MODE_PROC(flush_tlb_kernel_range_tt, + flush_tlb_kernel_range_common, start, end); +} + +void flush_tlb_kernel_vm(void) +{ + CHOOSE_MODE(flush_tlb_kernel_vm_tt(), + flush_tlb_kernel_range_common(start_vm, end_vm)); +} + +void __flush_tlb_one(unsigned long addr) +{ + CHOOSE_MODE_PROC(__flush_tlb_one_tt, __flush_tlb_one_skas, addr); +} + +void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, + unsigned long end) +{ + CHOOSE_MODE_PROC(flush_tlb_range_tt, flush_tlb_range_skas, vma, start, + end); +} + +void flush_tlb_mm(struct mm_struct *mm) +{ + CHOOSE_MODE_PROC(flush_tlb_mm_tt, flush_tlb_mm_skas, mm); +} + +void force_flush_all(void) +{ + CHOOSE_MODE(force_flush_all_tt(), force_flush_all_skas()); +} + +pgd_t *pgd_offset_proc(struct mm_struct *mm, unsigned long address) +{ + return(pgd_offset(mm, address)); +} + +pud_t *pud_offset_proc(pgd_t *pgd, unsigned long address) +{ + return(pud_offset(pgd, address)); +} + +pmd_t *pmd_offset_proc(pud_t *pud, unsigned long address) +{ + return(pmd_offset(pud, address)); +} + +pte_t *pte_offset_proc(pmd_t *pmd, unsigned long address) +{ + return(pte_offset_kernel(pmd, address)); +} + +pte_t *addr_pte(struct task_struct *task, unsigned long addr) +{ + pgd_t *pgd = pgd_offset(task->mm, addr); + pud_t *pud = pud_offset(pgd, addr); + pmd_t *pmd = pmd_offset(pud, addr); + + return(pte_offset_map(pmd, addr)); +} + +int add_mmap(unsigned long virt, unsigned long phys, unsigned long len, + int r, int w, int x, struct host_vm_op *ops, int index, + int last_filled, int data, + void (*do_ops)(int, struct host_vm_op *, int)) +{ + __u64 offset; + struct host_vm_op *last; + int fd; + + fd = phys_mapping(phys, &offset); + if(index != -1){ + last = &ops[index]; + if((last->type == MMAP) && + (last->u.mmap.addr + last->u.mmap.len == virt) && + (last->u.mmap.r == r) && (last->u.mmap.w == w) && + (last->u.mmap.x == x) && (last->u.mmap.fd == fd) && + (last->u.mmap.offset + last->u.mmap.len == offset)){ + last->u.mmap.len += len; + return(index); + } + } + + if(index == last_filled){ + (*do_ops)(data, ops, last_filled); + index = -1; + } + + ops[++index] = ((struct host_vm_op) { .type = MMAP, + .u = { .mmap = { + .addr = virt, + .len = len, + .r = r, + .w = w, + .x = x, + .fd = fd, + .offset = offset } + } }); + return(index); +} + +int add_munmap(unsigned long addr, unsigned long len, struct host_vm_op *ops, + int index, int last_filled, int data, + void (*do_ops)(int, struct host_vm_op *, int)) +{ + struct host_vm_op *last; + + if(index != -1){ + last = &ops[index]; + if((last->type == MUNMAP) && + (last->u.munmap.addr + last->u.mmap.len == addr)){ + last->u.munmap.len += len; + return(index); + } + } + + if(index == last_filled){ + (*do_ops)(data, ops, last_filled); + index = -1; + } + + ops[++index] = ((struct host_vm_op) { .type = MUNMAP, + .u = { .munmap = { + .addr = addr, + .len = len } } }); + return(index); +} + +int add_mprotect(unsigned long addr, unsigned long len, int r, int w, int x, + struct host_vm_op *ops, int index, int last_filled, int data, + void (*do_ops)(int, struct host_vm_op *, int)) +{ + struct host_vm_op *last; + + if(index != -1){ + last = &ops[index]; + if((last->type == MPROTECT) && + (last->u.mprotect.addr + last->u.mprotect.len == addr) && + (last->u.mprotect.r == r) && (last->u.mprotect.w == w) && + (last->u.mprotect.x == x)){ + last->u.mprotect.len += len; + return(index); + } + } + + if(index == last_filled){ + (*do_ops)(data, ops, last_filled); + index = -1; + } + + ops[++index] = ((struct host_vm_op) { .type = MPROTECT, + .u = { .mprotect = { + .addr = addr, + .len = len, + .r = r, + .w = w, + .x = x } } }); + return(index); +} |