#include #include #include #include #include #include #define ARCH_SPARC64 1 #define ARCH_IA64 2 #define ARCH_PPC64 3 #define ARCH ARCH_SPARC64 #if (ARCH == ARCH_SPARC64) #define PAGE_SHIFT 13 #define PMD_SHIFT (PAGE_SHIFT + (PAGE_SHIFT-3)) #define PMD_SIZE (1UL << PMD_SHIFT) #define PMD_MASK (~(PMD_SIZE-1)) #define PMD_BITS 11 #define PGDIR_SHIFT (PAGE_SHIFT + (PAGE_SHIFT-3) + PMD_BITS) #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE-1)) #define PUD_SHIFT PGDIR_SHIFT #define PTRS_PER_PUD 1 #define PUD_SIZE (1UL << PUD_SHIFT) #define PUD_MASK (~(PUD_SIZE-1)) #define pgd_addr_end(addr, end) \ ({ unsigned long __boundary = ((addr) + PGDIR_SIZE) & PGDIR_MASK; \ (__boundary - 1 < (end) - 1)? __boundary: (end); \ }) #define pud_addr_end(addr, end) (end) #define pmd_addr_end(addr, end) \ ({ unsigned long __boundary = ((addr) + PMD_SIZE) & PMD_MASK; \ (__boundary - 1 < (end) - 1)? __boundary: (end); \ }) #else #error please add definitions for your architecture #endif /* * Note: this doesn't free the actual pages themselves. That * has been handled earlier when unmapping all the memory regions. */ static inline void free_pte_range(unsigned long addr) { } static inline void free_pmd_range(unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) { unsigned long next; unsigned long start; start = addr; do { next = pmd_addr_end(addr, end); #if 1 printf("free_pgtables: free_pte_range() addr(%lx) next(%lx) end(%lx)\n", addr, next, end); #endif free_pte_range(addr); } while (addr = next, addr != end); start &= PUD_MASK; ceiling &= PUD_MASK; #if 1 printf("free_pmd_range: start(%lx) ceiling(%lx) ", start, ceiling); #endif if ((start < floor) || (end - 1 > ceiling - 1)) { #if 1 printf("SKIP pud_clear()\n"); #endif return; } #if 1 printf("DO pud_clear()\n"); #endif } static inline void free_pud_range(unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) { unsigned long next; unsigned long start; start = addr; do { next = pud_addr_end(addr, end); #if 1 printf("free_pud_range: free_pmd_range(%lx,%lx,%lx,%lx)\n", addr, next, floor, ceiling); #endif free_pmd_range(addr, next, floor, ceiling); } while (addr = next, addr != end); start &= PGDIR_MASK; ceiling &= PGDIR_MASK; #if 1 printf("free_pud_range: start(%lx) ceiling(%lx) ", start, ceiling); #endif if ((start < floor) || (end - 1 > ceiling - 1)) { #if 1 printf("SKIP pgd_clear()\n"); #endif return; } #if 1 printf("DO pgd_clear()\n"); #endif } /* * This function frees user-level page tables of a process. * Unlike other pagetable walks, some memory layouts might give end 0. * Must be called with pagetable lock held. */ static inline void free_pgd_range(unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) { unsigned long next; unsigned long start; addr &= PMD_MASK; if (addr < floor) { addr += PMD_SIZE; if (!addr) return; } ceiling &= PMD_MASK; if (end - 1 > ceiling - 1) end -= PMD_SIZE; if (addr > end - 1) return; start = addr; do { next = pgd_addr_end(addr, end); #if 1 printf("free_pgd_range: free_pud_range(%lx,%lx,%lx,%lx)\n", addr, next, floor, ceiling); #endif free_pud_range(addr, next, floor, ceiling); } while (addr = next, addr != end); if (1 /*!tlb_is_full_mm(tlb)*/) { #if 1 printf("free_pgd_range: Doing flush_tlb_pgtables(%lx,%lx)\n", start, end); #endif /*flush_tlb_pgtables(tlb->mm, start, end);*/ } } struct vm_area_struct { struct vm_area_struct *vm_next; unsigned long vm_start, vm_end; }; void free_pgtables(struct vm_area_struct *vma, unsigned long floor, unsigned long ceiling) { #if 1 struct vm_area_struct *tmp = vma; printf("VMA DUMP: "); while (tmp) { printf("[%lx:%lx] ", tmp->vm_start, tmp->vm_end); tmp = tmp->vm_next; } printf("\n"); #endif while (vma) { struct vm_area_struct *next = vma->vm_next; unsigned long addr = vma->vm_start; /* Optimization: gather nearby vmas into a single call down */ while (next && next->vm_start <= vma->vm_end + 2*PMD_SIZE) { vma = next; next = vma->vm_next; } #if 1 printf("free_pgtables: free_pgd_range(%lx,%lx,%lx,%lx)\n", addr, vma->vm_end, floor, next ? next->vm_start : ceiling); #endif free_pgd_range(addr, vma->vm_end, floor, next? next->vm_start: ceiling); vma = next; } } /* Note that we have to build the list in ascending order. */ static struct vm_area_struct *parse_file(FILE *fp) { struct vm_area_struct **pprev = NULL; struct vm_area_struct *mpnt, *head; unsigned long start, end; for (;;) { int ret = fscanf(fp, "%lx %lx", &start, &end); if (ret < 2 || ret == EOF) break; mpnt = malloc(sizeof(*mpnt)); if (mpnt == NULL) goto fail; #if 1 printf("parse_file: Building VMA %lx --> %lx\n", start, end); #endif mpnt->vm_next = NULL; mpnt->vm_start = start; mpnt->vm_end = end; if (pprev) *pprev = mpnt; else head = mpnt; pprev = &mpnt->vm_next; } return head; fail: while (head) { struct vm_area_struct *next = head->vm_next; free(head); head = next; } return NULL; } int main(int argc, char **argv, char **envp) { FILE *fp; struct vm_area_struct *vma; if (argc <= 1) return 1; fp = fopen(argv[1], "r"); if (fp == NULL) { perror("fopen()"); return 1; } if ((vma = parse_file(fp)) == NULL) return 1; free_pgtables(vma, 0, 0); return 0; }