diff options
author | GuanXuetao <gxt@mprc.pku.edu.cn> | 2011-01-15 05:17:56 -0500 |
---|---|---|
committer | GuanXuetao <gxt@mprc.pku.edu.cn> | 2011-03-16 21:19:09 -0400 |
commit | 56372b0b2f533c9a25bd40a0577405f6ddb7cff2 (patch) | |
tree | c428578bf7392232e946756cfe12e9144122c212 /arch/unicore32 | |
parent | b50f1704e9c441c58cf6dc05e72953ca30e1d4d2 (diff) |
unicore32 core architecture: mm related: fault handling
This patch implements fault handling of memory management.
Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/unicore32')
-rw-r--r-- | arch/unicore32/include/asm/mmu.h | 17 | ||||
-rw-r--r-- | arch/unicore32/include/asm/mmu_context.h | 87 | ||||
-rw-r--r-- | arch/unicore32/include/asm/pgalloc.h | 110 | ||||
-rw-r--r-- | arch/unicore32/include/asm/pgtable-hwdef.h | 55 | ||||
-rw-r--r-- | arch/unicore32/include/asm/pgtable.h | 317 | ||||
-rw-r--r-- | arch/unicore32/mm/alignment.c | 523 | ||||
-rw-r--r-- | arch/unicore32/mm/extable.c | 24 | ||||
-rw-r--r-- | arch/unicore32/mm/fault.c | 479 | ||||
-rw-r--r-- | arch/unicore32/mm/mmu.c | 533 | ||||
-rw-r--r-- | arch/unicore32/mm/pgd.c | 102 |
10 files changed, 2247 insertions, 0 deletions
diff --git a/arch/unicore32/include/asm/mmu.h b/arch/unicore32/include/asm/mmu.h new file mode 100644 index 000000000000..66fa341dc2c6 --- /dev/null +++ b/arch/unicore32/include/asm/mmu.h | |||
@@ -0,0 +1,17 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/include/asm/mmu.h | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #ifndef __UNICORE_MMU_H__ | ||
13 | #define __UNICORE_MMU_H__ | ||
14 | |||
15 | typedef unsigned long mm_context_t; | ||
16 | |||
17 | #endif | ||
diff --git a/arch/unicore32/include/asm/mmu_context.h b/arch/unicore32/include/asm/mmu_context.h new file mode 100644 index 000000000000..fb5e4c658f7a --- /dev/null +++ b/arch/unicore32/include/asm/mmu_context.h | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/include/asm/mmu_context.h | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #ifndef __UNICORE_MMU_CONTEXT_H__ | ||
13 | #define __UNICORE_MMU_CONTEXT_H__ | ||
14 | |||
15 | #include <linux/compiler.h> | ||
16 | #include <linux/sched.h> | ||
17 | #include <linux/io.h> | ||
18 | |||
19 | #include <asm/cacheflush.h> | ||
20 | #include <asm/cpu-single.h> | ||
21 | |||
22 | #define init_new_context(tsk, mm) 0 | ||
23 | |||
24 | #define destroy_context(mm) do { } while (0) | ||
25 | |||
26 | /* | ||
27 | * This is called when "tsk" is about to enter lazy TLB mode. | ||
28 | * | ||
29 | * mm: describes the currently active mm context | ||
30 | * tsk: task which is entering lazy tlb | ||
31 | * cpu: cpu number which is entering lazy tlb | ||
32 | * | ||
33 | * tsk->mm will be NULL | ||
34 | */ | ||
35 | static inline void | ||
36 | enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) | ||
37 | { | ||
38 | } | ||
39 | |||
40 | /* | ||
41 | * This is the actual mm switch as far as the scheduler | ||
42 | * is concerned. No registers are touched. We avoid | ||
43 | * calling the CPU specific function when the mm hasn't | ||
44 | * actually changed. | ||
45 | */ | ||
46 | static inline void | ||
47 | switch_mm(struct mm_struct *prev, struct mm_struct *next, | ||
48 | struct task_struct *tsk) | ||
49 | { | ||
50 | unsigned int cpu = smp_processor_id(); | ||
51 | |||
52 | if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next) | ||
53 | cpu_switch_mm(next->pgd, next); | ||
54 | } | ||
55 | |||
56 | #define deactivate_mm(tsk, mm) do { } while (0) | ||
57 | #define activate_mm(prev, next) switch_mm(prev, next, NULL) | ||
58 | |||
59 | /* | ||
60 | * We are inserting a "fake" vma for the user-accessible vector page so | ||
61 | * gdb and friends can get to it through ptrace and /proc/<pid>/mem. | ||
62 | * But we also want to remove it before the generic code gets to see it | ||
63 | * during process exit or the unmapping of it would cause total havoc. | ||
64 | * (the macro is used as remove_vma() is static to mm/mmap.c) | ||
65 | */ | ||
66 | #define arch_exit_mmap(mm) \ | ||
67 | do { \ | ||
68 | struct vm_area_struct *high_vma = find_vma(mm, 0xffff0000); \ | ||
69 | if (high_vma) { \ | ||
70 | BUG_ON(high_vma->vm_next); /* it should be last */ \ | ||
71 | if (high_vma->vm_prev) \ | ||
72 | high_vma->vm_prev->vm_next = NULL; \ | ||
73 | else \ | ||
74 | mm->mmap = NULL; \ | ||
75 | rb_erase(&high_vma->vm_rb, &mm->mm_rb); \ | ||
76 | mm->mmap_cache = NULL; \ | ||
77 | mm->map_count--; \ | ||
78 | remove_vma(high_vma); \ | ||
79 | } \ | ||
80 | } while (0) | ||
81 | |||
82 | static inline void arch_dup_mmap(struct mm_struct *oldmm, | ||
83 | struct mm_struct *mm) | ||
84 | { | ||
85 | } | ||
86 | |||
87 | #endif | ||
diff --git a/arch/unicore32/include/asm/pgalloc.h b/arch/unicore32/include/asm/pgalloc.h new file mode 100644 index 000000000000..0213e373a895 --- /dev/null +++ b/arch/unicore32/include/asm/pgalloc.h | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/include/asm/pgalloc.h | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #ifndef __UNICORE_PGALLOC_H__ | ||
13 | #define __UNICORE_PGALLOC_H__ | ||
14 | |||
15 | #include <asm/pgtable-hwdef.h> | ||
16 | #include <asm/processor.h> | ||
17 | #include <asm/cacheflush.h> | ||
18 | #include <asm/tlbflush.h> | ||
19 | |||
20 | #define check_pgt_cache() do { } while (0) | ||
21 | |||
22 | #define _PAGE_USER_TABLE (PMD_TYPE_TABLE | PMD_PRESENT) | ||
23 | #define _PAGE_KERNEL_TABLE (PMD_TYPE_TABLE | PMD_PRESENT) | ||
24 | |||
25 | extern pgd_t *get_pgd_slow(struct mm_struct *mm); | ||
26 | extern void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd); | ||
27 | |||
28 | #define pgd_alloc(mm) get_pgd_slow(mm) | ||
29 | #define pgd_free(mm, pgd) free_pgd_slow(mm, pgd) | ||
30 | |||
31 | #define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO) | ||
32 | |||
33 | /* | ||
34 | * Allocate one PTE table. | ||
35 | */ | ||
36 | static inline pte_t * | ||
37 | pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr) | ||
38 | { | ||
39 | pte_t *pte; | ||
40 | |||
41 | pte = (pte_t *)__get_free_page(PGALLOC_GFP); | ||
42 | if (pte) | ||
43 | clean_dcache_area(pte, PTRS_PER_PTE * sizeof(pte_t)); | ||
44 | |||
45 | return pte; | ||
46 | } | ||
47 | |||
48 | static inline pgtable_t | ||
49 | pte_alloc_one(struct mm_struct *mm, unsigned long addr) | ||
50 | { | ||
51 | struct page *pte; | ||
52 | |||
53 | pte = alloc_pages(PGALLOC_GFP, 0); | ||
54 | if (pte) { | ||
55 | if (!PageHighMem(pte)) { | ||
56 | void *page = page_address(pte); | ||
57 | clean_dcache_area(page, PTRS_PER_PTE * sizeof(pte_t)); | ||
58 | } | ||
59 | pgtable_page_ctor(pte); | ||
60 | } | ||
61 | |||
62 | return pte; | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | * Free one PTE table. | ||
67 | */ | ||
68 | static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte) | ||
69 | { | ||
70 | if (pte) | ||
71 | free_page((unsigned long)pte); | ||
72 | } | ||
73 | |||
74 | static inline void pte_free(struct mm_struct *mm, pgtable_t pte) | ||
75 | { | ||
76 | pgtable_page_dtor(pte); | ||
77 | __free_page(pte); | ||
78 | } | ||
79 | |||
80 | static inline void __pmd_populate(pmd_t *pmdp, unsigned long pmdval) | ||
81 | { | ||
82 | set_pmd(pmdp, __pmd(pmdval)); | ||
83 | flush_pmd_entry(pmdp); | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * Populate the pmdp entry with a pointer to the pte. This pmd is part | ||
88 | * of the mm address space. | ||
89 | */ | ||
90 | static inline void | ||
91 | pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep) | ||
92 | { | ||
93 | unsigned long pte_ptr = (unsigned long)ptep; | ||
94 | |||
95 | /* | ||
96 | * The pmd must be loaded with the physical | ||
97 | * address of the PTE table | ||
98 | */ | ||
99 | __pmd_populate(pmdp, __pa(pte_ptr) | _PAGE_KERNEL_TABLE); | ||
100 | } | ||
101 | |||
102 | static inline void | ||
103 | pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep) | ||
104 | { | ||
105 | __pmd_populate(pmdp, | ||
106 | page_to_pfn(ptep) << PAGE_SHIFT | _PAGE_USER_TABLE); | ||
107 | } | ||
108 | #define pmd_pgtable(pmd) pmd_page(pmd) | ||
109 | |||
110 | #endif | ||
diff --git a/arch/unicore32/include/asm/pgtable-hwdef.h b/arch/unicore32/include/asm/pgtable-hwdef.h new file mode 100644 index 000000000000..7314e859cca0 --- /dev/null +++ b/arch/unicore32/include/asm/pgtable-hwdef.h | |||
@@ -0,0 +1,55 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/include/asm/pgtable-hwdef.h | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #ifndef __UNICORE_PGTABLE_HWDEF_H__ | ||
13 | #define __UNICORE_PGTABLE_HWDEF_H__ | ||
14 | |||
15 | /* | ||
16 | * Hardware page table definitions. | ||
17 | * | ||
18 | * + Level 1 descriptor (PMD) | ||
19 | * - common | ||
20 | */ | ||
21 | #define PMD_TYPE_MASK (3 << 0) | ||
22 | #define PMD_TYPE_TABLE (0 << 0) | ||
23 | /*#define PMD_TYPE_LARGE (1 << 0) */ | ||
24 | #define PMD_TYPE_INVALID (2 << 0) | ||
25 | #define PMD_TYPE_SECT (3 << 0) | ||
26 | |||
27 | #define PMD_PRESENT (1 << 2) | ||
28 | #define PMD_YOUNG (1 << 3) | ||
29 | |||
30 | /*#define PMD_SECT_DIRTY (1 << 4) */ | ||
31 | #define PMD_SECT_CACHEABLE (1 << 5) | ||
32 | #define PMD_SECT_EXEC (1 << 6) | ||
33 | #define PMD_SECT_WRITE (1 << 7) | ||
34 | #define PMD_SECT_READ (1 << 8) | ||
35 | |||
36 | /* | ||
37 | * + Level 2 descriptor (PTE) | ||
38 | * - common | ||
39 | */ | ||
40 | #define PTE_TYPE_MASK (3 << 0) | ||
41 | #define PTE_TYPE_SMALL (0 << 0) | ||
42 | #define PTE_TYPE_MIDDLE (1 << 0) | ||
43 | #define PTE_TYPE_LARGE (2 << 0) | ||
44 | #define PTE_TYPE_INVALID (3 << 0) | ||
45 | |||
46 | #define PTE_PRESENT (1 << 2) | ||
47 | #define PTE_FILE (1 << 3) /* only when !PRESENT */ | ||
48 | #define PTE_YOUNG (1 << 3) | ||
49 | #define PTE_DIRTY (1 << 4) | ||
50 | #define PTE_CACHEABLE (1 << 5) | ||
51 | #define PTE_EXEC (1 << 6) | ||
52 | #define PTE_WRITE (1 << 7) | ||
53 | #define PTE_READ (1 << 8) | ||
54 | |||
55 | #endif | ||
diff --git a/arch/unicore32/include/asm/pgtable.h b/arch/unicore32/include/asm/pgtable.h new file mode 100644 index 000000000000..68b2f297ac97 --- /dev/null +++ b/arch/unicore32/include/asm/pgtable.h | |||
@@ -0,0 +1,317 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/include/asm/pgtable.h | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #ifndef __UNICORE_PGTABLE_H__ | ||
13 | #define __UNICORE_PGTABLE_H__ | ||
14 | |||
15 | #include <asm-generic/pgtable-nopmd.h> | ||
16 | #include <asm/cpu-single.h> | ||
17 | |||
18 | #include <asm/memory.h> | ||
19 | #include <asm/pgtable-hwdef.h> | ||
20 | |||
21 | /* | ||
22 | * Just any arbitrary offset to the start of the vmalloc VM area: the | ||
23 | * current 8MB value just means that there will be a 8MB "hole" after the | ||
24 | * physical memory until the kernel virtual memory starts. That means that | ||
25 | * any out-of-bounds memory accesses will hopefully be caught. | ||
26 | * The vmalloc() routines leaves a hole of 4kB between each vmalloced | ||
27 | * area for the same reason. ;) | ||
28 | * | ||
29 | * Note that platforms may override VMALLOC_START, but they must provide | ||
30 | * VMALLOC_END. VMALLOC_END defines the (exclusive) limit of this space, | ||
31 | * which may not overlap IO space. | ||
32 | */ | ||
33 | #ifndef VMALLOC_START | ||
34 | #define VMALLOC_OFFSET SZ_8M | ||
35 | #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) \ | ||
36 | & ~(VMALLOC_OFFSET-1)) | ||
37 | #define VMALLOC_END (0xff000000UL) | ||
38 | #endif | ||
39 | |||
40 | #define PTRS_PER_PTE 1024 | ||
41 | #define PTRS_PER_PGD 1024 | ||
42 | |||
43 | /* | ||
44 | * PGDIR_SHIFT determines what a third-level page table entry can map | ||
45 | */ | ||
46 | #define PGDIR_SHIFT 22 | ||
47 | |||
48 | #ifndef __ASSEMBLY__ | ||
49 | extern void __pte_error(const char *file, int line, unsigned long val); | ||
50 | extern void __pgd_error(const char *file, int line, unsigned long val); | ||
51 | |||
52 | #define pte_ERROR(pte) __pte_error(__FILE__, __LINE__, pte_val(pte)) | ||
53 | #define pgd_ERROR(pgd) __pgd_error(__FILE__, __LINE__, pgd_val(pgd)) | ||
54 | #endif /* !__ASSEMBLY__ */ | ||
55 | |||
56 | #define PGDIR_SIZE (1UL << PGDIR_SHIFT) | ||
57 | #define PGDIR_MASK (~(PGDIR_SIZE-1)) | ||
58 | |||
59 | /* | ||
60 | * This is the lowest virtual address we can permit any user space | ||
61 | * mapping to be mapped at. This is particularly important for | ||
62 | * non-high vector CPUs. | ||
63 | */ | ||
64 | #define FIRST_USER_ADDRESS PAGE_SIZE | ||
65 | |||
66 | #define FIRST_USER_PGD_NR 1 | ||
67 | #define USER_PTRS_PER_PGD ((TASK_SIZE/PGDIR_SIZE) - FIRST_USER_PGD_NR) | ||
68 | |||
69 | /* | ||
70 | * section address mask and size definitions. | ||
71 | */ | ||
72 | #define SECTION_SHIFT 22 | ||
73 | #define SECTION_SIZE (1UL << SECTION_SHIFT) | ||
74 | #define SECTION_MASK (~(SECTION_SIZE-1)) | ||
75 | |||
76 | #ifndef __ASSEMBLY__ | ||
77 | |||
78 | /* | ||
79 | * The pgprot_* and protection_map entries will be fixed up in runtime | ||
80 | * to include the cachable bits based on memory policy, as well as any | ||
81 | * architecture dependent bits. | ||
82 | */ | ||
83 | #define _PTE_DEFAULT (PTE_PRESENT | PTE_YOUNG | PTE_CACHEABLE) | ||
84 | |||
85 | extern pgprot_t pgprot_user; | ||
86 | extern pgprot_t pgprot_kernel; | ||
87 | |||
88 | #define PAGE_NONE pgprot_user | ||
89 | #define PAGE_SHARED __pgprot(pgprot_val(pgprot_user | PTE_READ \ | ||
90 | | PTE_WRITE) | ||
91 | #define PAGE_SHARED_EXEC __pgprot(pgprot_val(pgprot_user | PTE_READ \ | ||
92 | | PTE_WRITE \ | ||
93 | | PTE_EXEC) | ||
94 | #define PAGE_COPY __pgprot(pgprot_val(pgprot_user | PTE_READ) | ||
95 | #define PAGE_COPY_EXEC __pgprot(pgprot_val(pgprot_user | PTE_READ \ | ||
96 | | PTE_EXEC) | ||
97 | #define PAGE_READONLY __pgprot(pgprot_val(pgprot_user | PTE_READ) | ||
98 | #define PAGE_READONLY_EXEC __pgprot(pgprot_val(pgprot_user | PTE_READ \ | ||
99 | | PTE_EXEC) | ||
100 | #define PAGE_KERNEL pgprot_kernel | ||
101 | #define PAGE_KERNEL_EXEC __pgprot(pgprot_val(pgprot_kernel | PTE_EXEC)) | ||
102 | |||
103 | #define __PAGE_NONE __pgprot(_PTE_DEFAULT) | ||
104 | #define __PAGE_SHARED __pgprot(_PTE_DEFAULT | PTE_READ \ | ||
105 | | PTE_WRITE) | ||
106 | #define __PAGE_SHARED_EXEC __pgprot(_PTE_DEFAULT | PTE_READ \ | ||
107 | | PTE_WRITE \ | ||
108 | | PTE_EXEC) | ||
109 | #define __PAGE_COPY __pgprot(_PTE_DEFAULT | PTE_READ) | ||
110 | #define __PAGE_COPY_EXEC __pgprot(_PTE_DEFAULT | PTE_READ \ | ||
111 | | PTE_EXEC) | ||
112 | #define __PAGE_READONLY __pgprot(_PTE_DEFAULT | PTE_READ) | ||
113 | #define __PAGE_READONLY_EXEC __pgprot(_PTE_DEFAULT | PTE_READ \ | ||
114 | | PTE_EXEC) | ||
115 | |||
116 | #endif /* __ASSEMBLY__ */ | ||
117 | |||
118 | /* | ||
119 | * The table below defines the page protection levels that we insert into our | ||
120 | * Linux page table version. These get translated into the best that the | ||
121 | * architecture can perform. Note that on UniCore hardware: | ||
122 | * 1) We cannot do execute protection | ||
123 | * 2) If we could do execute protection, then read is implied | ||
124 | * 3) write implies read permissions | ||
125 | */ | ||
126 | #define __P000 __PAGE_NONE | ||
127 | #define __P001 __PAGE_READONLY | ||
128 | #define __P010 __PAGE_COPY | ||
129 | #define __P011 __PAGE_COPY | ||
130 | #define __P100 __PAGE_READONLY_EXEC | ||
131 | #define __P101 __PAGE_READONLY_EXEC | ||
132 | #define __P110 __PAGE_COPY_EXEC | ||
133 | #define __P111 __PAGE_COPY_EXEC | ||
134 | |||
135 | #define __S000 __PAGE_NONE | ||
136 | #define __S001 __PAGE_READONLY | ||
137 | #define __S010 __PAGE_SHARED | ||
138 | #define __S011 __PAGE_SHARED | ||
139 | #define __S100 __PAGE_READONLY_EXEC | ||
140 | #define __S101 __PAGE_READONLY_EXEC | ||
141 | #define __S110 __PAGE_SHARED_EXEC | ||
142 | #define __S111 __PAGE_SHARED_EXEC | ||
143 | |||
144 | #ifndef __ASSEMBLY__ | ||
145 | /* | ||
146 | * ZERO_PAGE is a global shared page that is always zero: used | ||
147 | * for zero-mapped memory areas etc.. | ||
148 | */ | ||
149 | extern struct page *empty_zero_page; | ||
150 | #define ZERO_PAGE(vaddr) (empty_zero_page) | ||
151 | |||
152 | #define pte_pfn(pte) (pte_val(pte) >> PAGE_SHIFT) | ||
153 | #define pfn_pte(pfn, prot) (__pte(((pfn) << PAGE_SHIFT) \ | ||
154 | | pgprot_val(prot))) | ||
155 | |||
156 | #define pte_none(pte) (!pte_val(pte)) | ||
157 | #define pte_clear(mm, addr, ptep) set_pte(ptep, __pte(0)) | ||
158 | #define pte_page(pte) (pfn_to_page(pte_pfn(pte))) | ||
159 | #define pte_offset_kernel(dir, addr) (pmd_page_vaddr(*(dir)) \ | ||
160 | + __pte_index(addr)) | ||
161 | |||
162 | #define pte_offset_map(dir, addr) (pmd_page_vaddr(*(dir)) \ | ||
163 | + __pte_index(addr)) | ||
164 | #define pte_unmap(pte) do { } while (0) | ||
165 | |||
166 | #define set_pte(ptep, pte) cpu_set_pte(ptep, pte) | ||
167 | |||
168 | #define set_pte_at(mm, addr, ptep, pteval) \ | ||
169 | do { \ | ||
170 | set_pte(ptep, pteval); \ | ||
171 | } while (0) | ||
172 | |||
173 | /* | ||
174 | * The following only work if pte_present() is true. | ||
175 | * Undefined behaviour if not.. | ||
176 | */ | ||
177 | #define pte_present(pte) (pte_val(pte) & PTE_PRESENT) | ||
178 | #define pte_write(pte) (pte_val(pte) & PTE_WRITE) | ||
179 | #define pte_dirty(pte) (pte_val(pte) & PTE_DIRTY) | ||
180 | #define pte_young(pte) (pte_val(pte) & PTE_YOUNG) | ||
181 | #define pte_exec(pte) (pte_val(pte) & PTE_EXEC) | ||
182 | #define pte_special(pte) (0) | ||
183 | |||
184 | #define PTE_BIT_FUNC(fn, op) \ | ||
185 | static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; } | ||
186 | |||
187 | PTE_BIT_FUNC(wrprotect, &= ~PTE_WRITE); | ||
188 | PTE_BIT_FUNC(mkwrite, |= PTE_WRITE); | ||
189 | PTE_BIT_FUNC(mkclean, &= ~PTE_DIRTY); | ||
190 | PTE_BIT_FUNC(mkdirty, |= PTE_DIRTY); | ||
191 | PTE_BIT_FUNC(mkold, &= ~PTE_YOUNG); | ||
192 | PTE_BIT_FUNC(mkyoung, |= PTE_YOUNG); | ||
193 | |||
194 | static inline pte_t pte_mkspecial(pte_t pte) { return pte; } | ||
195 | |||
196 | /* | ||
197 | * Mark the prot value as uncacheable. | ||
198 | */ | ||
199 | #define pgprot_noncached(prot) \ | ||
200 | __pgprot(pgprot_val(prot) & ~PTE_CACHEABLE) | ||
201 | #define pgprot_writecombine(prot) \ | ||
202 | __pgprot(pgprot_val(prot) & ~PTE_CACHEABLE) | ||
203 | #define pgprot_dmacoherent(prot) \ | ||
204 | __pgprot(pgprot_val(prot) & ~PTE_CACHEABLE) | ||
205 | |||
206 | #define pmd_none(pmd) (!pmd_val(pmd)) | ||
207 | #define pmd_present(pmd) (pmd_val(pmd) & PMD_PRESENT) | ||
208 | #define pmd_bad(pmd) (((pmd_val(pmd) & \ | ||
209 | (PMD_PRESENT | PMD_TYPE_MASK)) \ | ||
210 | != (PMD_PRESENT | PMD_TYPE_TABLE))) | ||
211 | |||
212 | #define set_pmd(pmdpd, pmdval) \ | ||
213 | do { \ | ||
214 | *(pmdpd) = pmdval; \ | ||
215 | } while (0) | ||
216 | |||
217 | #define pmd_clear(pmdp) \ | ||
218 | do { \ | ||
219 | set_pmd(pmdp, __pmd(0));\ | ||
220 | clean_pmd_entry(pmdp); \ | ||
221 | } while (0) | ||
222 | |||
223 | #define pmd_page_vaddr(pmd) ((pte_t *)__va(pmd_val(pmd) & PAGE_MASK)) | ||
224 | #define pmd_page(pmd) pfn_to_page(__phys_to_pfn(pmd_val(pmd))) | ||
225 | |||
226 | /* | ||
227 | * Conversion functions: convert a page and protection to a page entry, | ||
228 | * and a page entry and page directory to the page they refer to. | ||
229 | */ | ||
230 | #define mk_pte(page, prot) pfn_pte(page_to_pfn(page), prot) | ||
231 | |||
232 | /* to find an entry in a page-table-directory */ | ||
233 | #define pgd_index(addr) ((addr) >> PGDIR_SHIFT) | ||
234 | |||
235 | #define pgd_offset(mm, addr) ((mm)->pgd+pgd_index(addr)) | ||
236 | |||
237 | /* to find an entry in a kernel page-table-directory */ | ||
238 | #define pgd_offset_k(addr) pgd_offset(&init_mm, addr) | ||
239 | |||
240 | /* Find an entry in the third-level page table.. */ | ||
241 | #define __pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) | ||
242 | |||
243 | static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) | ||
244 | { | ||
245 | const unsigned long mask = PTE_EXEC | PTE_WRITE | PTE_READ; | ||
246 | pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask); | ||
247 | return pte; | ||
248 | } | ||
249 | |||
250 | extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; | ||
251 | |||
252 | /* | ||
253 | * Encode and decode a swap entry. Swap entries are stored in the Linux | ||
254 | * page tables as follows: | ||
255 | * | ||
256 | * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 | ||
257 | * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 | ||
258 | * <--------------- offset --------------> <--- type --> 0 0 0 0 0 | ||
259 | * | ||
260 | * This gives us up to 127 swap files and 32GB per swap file. Note that | ||
261 | * the offset field is always non-zero. | ||
262 | */ | ||
263 | #define __SWP_TYPE_SHIFT 5 | ||
264 | #define __SWP_TYPE_BITS 7 | ||
265 | #define __SWP_TYPE_MASK ((1 << __SWP_TYPE_BITS) - 1) | ||
266 | #define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT) | ||
267 | |||
268 | #define __swp_type(x) (((x).val >> __SWP_TYPE_SHIFT) \ | ||
269 | & __SWP_TYPE_MASK) | ||
270 | #define __swp_offset(x) ((x).val >> __SWP_OFFSET_SHIFT) | ||
271 | #define __swp_entry(type, offset) ((swp_entry_t) { \ | ||
272 | ((type) << __SWP_TYPE_SHIFT) | \ | ||
273 | ((offset) << __SWP_OFFSET_SHIFT) }) | ||
274 | |||
275 | #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) }) | ||
276 | #define __swp_entry_to_pte(swp) ((pte_t) { (swp).val }) | ||
277 | |||
278 | /* | ||
279 | * It is an error for the kernel to have more swap files than we can | ||
280 | * encode in the PTEs. This ensures that we know when MAX_SWAPFILES | ||
281 | * is increased beyond what we presently support. | ||
282 | */ | ||
283 | #define MAX_SWAPFILES_CHECK() \ | ||
284 | BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > __SWP_TYPE_BITS) | ||
285 | |||
286 | /* | ||
287 | * Encode and decode a file entry. File entries are stored in the Linux | ||
288 | * page tables as follows: | ||
289 | * | ||
290 | * 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 | ||
291 | * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 | ||
292 | * <----------------------- offset ----------------------> 1 0 0 0 | ||
293 | */ | ||
294 | #define pte_file(pte) (pte_val(pte) & PTE_FILE) | ||
295 | #define pte_to_pgoff(x) (pte_val(x) >> 4) | ||
296 | #define pgoff_to_pte(x) __pte(((x) << 4) | PTE_FILE) | ||
297 | |||
298 | #define PTE_FILE_MAX_BITS 28 | ||
299 | |||
300 | /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ | ||
301 | /* FIXME: this is not correct */ | ||
302 | #define kern_addr_valid(addr) (1) | ||
303 | |||
304 | #include <asm-generic/pgtable.h> | ||
305 | |||
306 | /* | ||
307 | * remap a physical page `pfn' of size `size' with page protection `prot' | ||
308 | * into virtual address `from' | ||
309 | */ | ||
310 | #define io_remap_pfn_range(vma, from, pfn, size, prot) \ | ||
311 | remap_pfn_range(vma, from, pfn, size, prot) | ||
312 | |||
313 | #define pgtable_cache_init() do { } while (0) | ||
314 | |||
315 | #endif /* !__ASSEMBLY__ */ | ||
316 | |||
317 | #endif /* __UNICORE_PGTABLE_H__ */ | ||
diff --git a/arch/unicore32/mm/alignment.c b/arch/unicore32/mm/alignment.c new file mode 100644 index 000000000000..28f576d733ee --- /dev/null +++ b/arch/unicore32/mm/alignment.c | |||
@@ -0,0 +1,523 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/mm/alignment.c | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | /* | ||
13 | * TODO: | ||
14 | * FPU ldm/stm not handling | ||
15 | */ | ||
16 | #include <linux/compiler.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/errno.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/uaccess.h> | ||
23 | |||
24 | #include <asm/tlbflush.h> | ||
25 | #include <asm/unaligned.h> | ||
26 | |||
27 | #define CODING_BITS(i) (i & 0xe0000120) | ||
28 | |||
29 | #define LDST_P_BIT(i) (i & (1 << 28)) /* Preindex */ | ||
30 | #define LDST_U_BIT(i) (i & (1 << 27)) /* Add offset */ | ||
31 | #define LDST_W_BIT(i) (i & (1 << 25)) /* Writeback */ | ||
32 | #define LDST_L_BIT(i) (i & (1 << 24)) /* Load */ | ||
33 | |||
34 | #define LDST_P_EQ_U(i) ((((i) ^ ((i) >> 1)) & (1 << 27)) == 0) | ||
35 | |||
36 | #define LDSTH_I_BIT(i) (i & (1 << 26)) /* half-word immed */ | ||
37 | #define LDM_S_BIT(i) (i & (1 << 26)) /* write ASR from BSR */ | ||
38 | #define LDM_H_BIT(i) (i & (1 << 6)) /* select r0-r15 or r16-r31 */ | ||
39 | |||
40 | #define RN_BITS(i) ((i >> 19) & 31) /* Rn */ | ||
41 | #define RD_BITS(i) ((i >> 14) & 31) /* Rd */ | ||
42 | #define RM_BITS(i) (i & 31) /* Rm */ | ||
43 | |||
44 | #define REGMASK_BITS(i) (((i & 0x7fe00) >> 3) | (i & 0x3f)) | ||
45 | #define OFFSET_BITS(i) (i & 0x03fff) | ||
46 | |||
47 | #define SHIFT_BITS(i) ((i >> 9) & 0x1f) | ||
48 | #define SHIFT_TYPE(i) (i & 0xc0) | ||
49 | #define SHIFT_LSL 0x00 | ||
50 | #define SHIFT_LSR 0x40 | ||
51 | #define SHIFT_ASR 0x80 | ||
52 | #define SHIFT_RORRRX 0xc0 | ||
53 | |||
54 | union offset_union { | ||
55 | unsigned long un; | ||
56 | signed long sn; | ||
57 | }; | ||
58 | |||
59 | #define TYPE_ERROR 0 | ||
60 | #define TYPE_FAULT 1 | ||
61 | #define TYPE_LDST 2 | ||
62 | #define TYPE_DONE 3 | ||
63 | #define TYPE_SWAP 4 | ||
64 | #define TYPE_COLS 5 /* Coprocessor load/store */ | ||
65 | |||
66 | #define get8_unaligned_check(val, addr, err) \ | ||
67 | __asm__( \ | ||
68 | "1: ldb.u %1, [%2], #1\n" \ | ||
69 | "2:\n" \ | ||
70 | " .pushsection .fixup,\"ax\"\n" \ | ||
71 | " .align 2\n" \ | ||
72 | "3: mov %0, #1\n" \ | ||
73 | " b 2b\n" \ | ||
74 | " .popsection\n" \ | ||
75 | " .pushsection __ex_table,\"a\"\n" \ | ||
76 | " .align 3\n" \ | ||
77 | " .long 1b, 3b\n" \ | ||
78 | " .popsection\n" \ | ||
79 | : "=r" (err), "=&r" (val), "=r" (addr) \ | ||
80 | : "0" (err), "2" (addr)) | ||
81 | |||
82 | #define get8t_unaligned_check(val, addr, err) \ | ||
83 | __asm__( \ | ||
84 | "1: ldb.u %1, [%2], #1\n" \ | ||
85 | "2:\n" \ | ||
86 | " .pushsection .fixup,\"ax\"\n" \ | ||
87 | " .align 2\n" \ | ||
88 | "3: mov %0, #1\n" \ | ||
89 | " b 2b\n" \ | ||
90 | " .popsection\n" \ | ||
91 | " .pushsection __ex_table,\"a\"\n" \ | ||
92 | " .align 3\n" \ | ||
93 | " .long 1b, 3b\n" \ | ||
94 | " .popsection\n" \ | ||
95 | : "=r" (err), "=&r" (val), "=r" (addr) \ | ||
96 | : "0" (err), "2" (addr)) | ||
97 | |||
98 | #define get16_unaligned_check(val, addr) \ | ||
99 | do { \ | ||
100 | unsigned int err = 0, v, a = addr; \ | ||
101 | get8_unaligned_check(val, a, err); \ | ||
102 | get8_unaligned_check(v, a, err); \ | ||
103 | val |= v << 8; \ | ||
104 | if (err) \ | ||
105 | goto fault; \ | ||
106 | } while (0) | ||
107 | |||
108 | #define put16_unaligned_check(val, addr) \ | ||
109 | do { \ | ||
110 | unsigned int err = 0, v = val, a = addr; \ | ||
111 | __asm__( \ | ||
112 | "1: stb.u %1, [%2], #1\n" \ | ||
113 | " mov %1, %1 >> #8\n" \ | ||
114 | "2: stb.u %1, [%2]\n" \ | ||
115 | "3:\n" \ | ||
116 | " .pushsection .fixup,\"ax\"\n" \ | ||
117 | " .align 2\n" \ | ||
118 | "4: mov %0, #1\n" \ | ||
119 | " b 3b\n" \ | ||
120 | " .popsection\n" \ | ||
121 | " .pushsection __ex_table,\"a\"\n" \ | ||
122 | " .align 3\n" \ | ||
123 | " .long 1b, 4b\n" \ | ||
124 | " .long 2b, 4b\n" \ | ||
125 | " .popsection\n" \ | ||
126 | : "=r" (err), "=&r" (v), "=&r" (a) \ | ||
127 | : "0" (err), "1" (v), "2" (a)); \ | ||
128 | if (err) \ | ||
129 | goto fault; \ | ||
130 | } while (0) | ||
131 | |||
132 | #define __put32_unaligned_check(ins, val, addr) \ | ||
133 | do { \ | ||
134 | unsigned int err = 0, v = val, a = addr; \ | ||
135 | __asm__( \ | ||
136 | "1: "ins" %1, [%2], #1\n" \ | ||
137 | " mov %1, %1 >> #8\n" \ | ||
138 | "2: "ins" %1, [%2], #1\n" \ | ||
139 | " mov %1, %1 >> #8\n" \ | ||
140 | "3: "ins" %1, [%2], #1\n" \ | ||
141 | " mov %1, %1 >> #8\n" \ | ||
142 | "4: "ins" %1, [%2]\n" \ | ||
143 | "5:\n" \ | ||
144 | " .pushsection .fixup,\"ax\"\n" \ | ||
145 | " .align 2\n" \ | ||
146 | "6: mov %0, #1\n" \ | ||
147 | " b 5b\n" \ | ||
148 | " .popsection\n" \ | ||
149 | " .pushsection __ex_table,\"a\"\n" \ | ||
150 | " .align 3\n" \ | ||
151 | " .long 1b, 6b\n" \ | ||
152 | " .long 2b, 6b\n" \ | ||
153 | " .long 3b, 6b\n" \ | ||
154 | " .long 4b, 6b\n" \ | ||
155 | " .popsection\n" \ | ||
156 | : "=r" (err), "=&r" (v), "=&r" (a) \ | ||
157 | : "0" (err), "1" (v), "2" (a)); \ | ||
158 | if (err) \ | ||
159 | goto fault; \ | ||
160 | } while (0) | ||
161 | |||
162 | #define get32_unaligned_check(val, addr) \ | ||
163 | do { \ | ||
164 | unsigned int err = 0, v, a = addr; \ | ||
165 | get8_unaligned_check(val, a, err); \ | ||
166 | get8_unaligned_check(v, a, err); \ | ||
167 | val |= v << 8; \ | ||
168 | get8_unaligned_check(v, a, err); \ | ||
169 | val |= v << 16; \ | ||
170 | get8_unaligned_check(v, a, err); \ | ||
171 | val |= v << 24; \ | ||
172 | if (err) \ | ||
173 | goto fault; \ | ||
174 | } while (0) | ||
175 | |||
176 | #define put32_unaligned_check(val, addr) \ | ||
177 | __put32_unaligned_check("stb.u", val, addr) | ||
178 | |||
179 | #define get32t_unaligned_check(val, addr) \ | ||
180 | do { \ | ||
181 | unsigned int err = 0, v, a = addr; \ | ||
182 | get8t_unaligned_check(val, a, err); \ | ||
183 | get8t_unaligned_check(v, a, err); \ | ||
184 | val |= v << 8; \ | ||
185 | get8t_unaligned_check(v, a, err); \ | ||
186 | val |= v << 16; \ | ||
187 | get8t_unaligned_check(v, a, err); \ | ||
188 | val |= v << 24; \ | ||
189 | if (err) \ | ||
190 | goto fault; \ | ||
191 | } while (0) | ||
192 | |||
193 | #define put32t_unaligned_check(val, addr) \ | ||
194 | __put32_unaligned_check("stb.u", val, addr) | ||
195 | |||
196 | static void | ||
197 | do_alignment_finish_ldst(unsigned long addr, unsigned long instr, | ||
198 | struct pt_regs *regs, union offset_union offset) | ||
199 | { | ||
200 | if (!LDST_U_BIT(instr)) | ||
201 | offset.un = -offset.un; | ||
202 | |||
203 | if (!LDST_P_BIT(instr)) | ||
204 | addr += offset.un; | ||
205 | |||
206 | if (!LDST_P_BIT(instr) || LDST_W_BIT(instr)) | ||
207 | regs->uregs[RN_BITS(instr)] = addr; | ||
208 | } | ||
209 | |||
210 | static int | ||
211 | do_alignment_ldrhstrh(unsigned long addr, unsigned long instr, | ||
212 | struct pt_regs *regs) | ||
213 | { | ||
214 | unsigned int rd = RD_BITS(instr); | ||
215 | |||
216 | /* old value 0x40002120, can't judge swap instr correctly */ | ||
217 | if ((instr & 0x4b003fe0) == 0x40000120) | ||
218 | goto swp; | ||
219 | |||
220 | if (LDST_L_BIT(instr)) { | ||
221 | unsigned long val; | ||
222 | get16_unaligned_check(val, addr); | ||
223 | |||
224 | /* signed half-word? */ | ||
225 | if (instr & 0x80) | ||
226 | val = (signed long)((signed short)val); | ||
227 | |||
228 | regs->uregs[rd] = val; | ||
229 | } else | ||
230 | put16_unaligned_check(regs->uregs[rd], addr); | ||
231 | |||
232 | return TYPE_LDST; | ||
233 | |||
234 | swp: | ||
235 | /* only handle swap word | ||
236 | * for swap byte should not active this alignment exception */ | ||
237 | get32_unaligned_check(regs->uregs[RD_BITS(instr)], addr); | ||
238 | put32_unaligned_check(regs->uregs[RM_BITS(instr)], addr); | ||
239 | return TYPE_SWAP; | ||
240 | |||
241 | fault: | ||
242 | return TYPE_FAULT; | ||
243 | } | ||
244 | |||
245 | static int | ||
246 | do_alignment_ldrstr(unsigned long addr, unsigned long instr, | ||
247 | struct pt_regs *regs) | ||
248 | { | ||
249 | unsigned int rd = RD_BITS(instr); | ||
250 | |||
251 | if (!LDST_P_BIT(instr) && LDST_W_BIT(instr)) | ||
252 | goto trans; | ||
253 | |||
254 | if (LDST_L_BIT(instr)) | ||
255 | get32_unaligned_check(regs->uregs[rd], addr); | ||
256 | else | ||
257 | put32_unaligned_check(regs->uregs[rd], addr); | ||
258 | return TYPE_LDST; | ||
259 | |||
260 | trans: | ||
261 | if (LDST_L_BIT(instr)) | ||
262 | get32t_unaligned_check(regs->uregs[rd], addr); | ||
263 | else | ||
264 | put32t_unaligned_check(regs->uregs[rd], addr); | ||
265 | return TYPE_LDST; | ||
266 | |||
267 | fault: | ||
268 | return TYPE_FAULT; | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * LDM/STM alignment handler. | ||
273 | * | ||
274 | * There are 4 variants of this instruction: | ||
275 | * | ||
276 | * B = rn pointer before instruction, A = rn pointer after instruction | ||
277 | * ------ increasing address -----> | ||
278 | * | | r0 | r1 | ... | rx | | | ||
279 | * PU = 01 B A | ||
280 | * PU = 11 B A | ||
281 | * PU = 00 A B | ||
282 | * PU = 10 A B | ||
283 | */ | ||
284 | static int | ||
285 | do_alignment_ldmstm(unsigned long addr, unsigned long instr, | ||
286 | struct pt_regs *regs) | ||
287 | { | ||
288 | unsigned int rd, rn, pc_correction, reg_correction, nr_regs, regbits; | ||
289 | unsigned long eaddr, newaddr; | ||
290 | |||
291 | if (LDM_S_BIT(instr)) | ||
292 | goto bad; | ||
293 | |||
294 | pc_correction = 4; /* processor implementation defined */ | ||
295 | |||
296 | /* count the number of registers in the mask to be transferred */ | ||
297 | nr_regs = hweight16(REGMASK_BITS(instr)) * 4; | ||
298 | |||
299 | rn = RN_BITS(instr); | ||
300 | newaddr = eaddr = regs->uregs[rn]; | ||
301 | |||
302 | if (!LDST_U_BIT(instr)) | ||
303 | nr_regs = -nr_regs; | ||
304 | newaddr += nr_regs; | ||
305 | if (!LDST_U_BIT(instr)) | ||
306 | eaddr = newaddr; | ||
307 | |||
308 | if (LDST_P_EQ_U(instr)) /* U = P */ | ||
309 | eaddr += 4; | ||
310 | |||
311 | /* | ||
312 | * This is a "hint" - we already have eaddr worked out by the | ||
313 | * processor for us. | ||
314 | */ | ||
315 | if (addr != eaddr) { | ||
316 | printk(KERN_ERR "LDMSTM: PC = %08lx, instr = %08lx, " | ||
317 | "addr = %08lx, eaddr = %08lx\n", | ||
318 | instruction_pointer(regs), instr, addr, eaddr); | ||
319 | show_regs(regs); | ||
320 | } | ||
321 | |||
322 | if (LDM_H_BIT(instr)) | ||
323 | reg_correction = 0x10; | ||
324 | else | ||
325 | reg_correction = 0x00; | ||
326 | |||
327 | for (regbits = REGMASK_BITS(instr), rd = 0; regbits; | ||
328 | regbits >>= 1, rd += 1) | ||
329 | if (regbits & 1) { | ||
330 | if (LDST_L_BIT(instr)) | ||
331 | get32_unaligned_check(regs-> | ||
332 | uregs[rd + reg_correction], eaddr); | ||
333 | else | ||
334 | put32_unaligned_check(regs-> | ||
335 | uregs[rd + reg_correction], eaddr); | ||
336 | eaddr += 4; | ||
337 | } | ||
338 | |||
339 | if (LDST_W_BIT(instr)) | ||
340 | regs->uregs[rn] = newaddr; | ||
341 | return TYPE_DONE; | ||
342 | |||
343 | fault: | ||
344 | regs->UCreg_pc -= pc_correction; | ||
345 | return TYPE_FAULT; | ||
346 | |||
347 | bad: | ||
348 | printk(KERN_ERR "Alignment trap: not handling ldm with s-bit set\n"); | ||
349 | return TYPE_ERROR; | ||
350 | } | ||
351 | |||
352 | static int | ||
353 | do_alignment(unsigned long addr, unsigned int error_code, struct pt_regs *regs) | ||
354 | { | ||
355 | union offset_union offset; | ||
356 | unsigned long instr, instrptr; | ||
357 | int (*handler) (unsigned long addr, unsigned long instr, | ||
358 | struct pt_regs *regs); | ||
359 | unsigned int type; | ||
360 | |||
361 | instrptr = instruction_pointer(regs); | ||
362 | if (instrptr >= PAGE_OFFSET) | ||
363 | instr = *(unsigned long *)instrptr; | ||
364 | else { | ||
365 | __asm__ __volatile__( | ||
366 | "ldw.u %0, [%1]\n" | ||
367 | : "=&r"(instr) | ||
368 | : "r"(instrptr)); | ||
369 | } | ||
370 | |||
371 | regs->UCreg_pc += 4; | ||
372 | |||
373 | switch (CODING_BITS(instr)) { | ||
374 | case 0x40000120: /* ldrh or strh */ | ||
375 | if (LDSTH_I_BIT(instr)) | ||
376 | offset.un = (instr & 0x3e00) >> 4 | (instr & 31); | ||
377 | else | ||
378 | offset.un = regs->uregs[RM_BITS(instr)]; | ||
379 | handler = do_alignment_ldrhstrh; | ||
380 | break; | ||
381 | |||
382 | case 0x60000000: /* ldr or str immediate */ | ||
383 | case 0x60000100: /* ldr or str immediate */ | ||
384 | case 0x60000020: /* ldr or str immediate */ | ||
385 | case 0x60000120: /* ldr or str immediate */ | ||
386 | offset.un = OFFSET_BITS(instr); | ||
387 | handler = do_alignment_ldrstr; | ||
388 | break; | ||
389 | |||
390 | case 0x40000000: /* ldr or str register */ | ||
391 | offset.un = regs->uregs[RM_BITS(instr)]; | ||
392 | { | ||
393 | unsigned int shiftval = SHIFT_BITS(instr); | ||
394 | |||
395 | switch (SHIFT_TYPE(instr)) { | ||
396 | case SHIFT_LSL: | ||
397 | offset.un <<= shiftval; | ||
398 | break; | ||
399 | |||
400 | case SHIFT_LSR: | ||
401 | offset.un >>= shiftval; | ||
402 | break; | ||
403 | |||
404 | case SHIFT_ASR: | ||
405 | offset.sn >>= shiftval; | ||
406 | break; | ||
407 | |||
408 | case SHIFT_RORRRX: | ||
409 | if (shiftval == 0) { | ||
410 | offset.un >>= 1; | ||
411 | if (regs->UCreg_asr & PSR_C_BIT) | ||
412 | offset.un |= 1 << 31; | ||
413 | } else | ||
414 | offset.un = offset.un >> shiftval | | ||
415 | offset.un << (32 - shiftval); | ||
416 | break; | ||
417 | } | ||
418 | } | ||
419 | handler = do_alignment_ldrstr; | ||
420 | break; | ||
421 | |||
422 | case 0x80000000: /* ldm or stm */ | ||
423 | case 0x80000020: /* ldm or stm */ | ||
424 | handler = do_alignment_ldmstm; | ||
425 | break; | ||
426 | |||
427 | default: | ||
428 | goto bad; | ||
429 | } | ||
430 | |||
431 | type = handler(addr, instr, regs); | ||
432 | |||
433 | if (type == TYPE_ERROR || type == TYPE_FAULT) | ||
434 | goto bad_or_fault; | ||
435 | |||
436 | if (type == TYPE_LDST) | ||
437 | do_alignment_finish_ldst(addr, instr, regs, offset); | ||
438 | |||
439 | return 0; | ||
440 | |||
441 | bad_or_fault: | ||
442 | if (type == TYPE_ERROR) | ||
443 | goto bad; | ||
444 | regs->UCreg_pc -= 4; | ||
445 | /* | ||
446 | * We got a fault - fix it up, or die. | ||
447 | */ | ||
448 | do_bad_area(addr, error_code, regs); | ||
449 | return 0; | ||
450 | |||
451 | bad: | ||
452 | /* | ||
453 | * Oops, we didn't handle the instruction. | ||
454 | * However, we must handle fpu instr firstly. | ||
455 | */ | ||
456 | #ifdef CONFIG_UNICORE_FPU_F64 | ||
457 | /* handle co.load/store */ | ||
458 | #define CODING_COLS 0xc0000000 | ||
459 | #define COLS_OFFSET_BITS(i) (i & 0x1FF) | ||
460 | #define COLS_L_BITS(i) (i & (1<<24)) | ||
461 | #define COLS_FN_BITS(i) ((i>>14) & 31) | ||
462 | if ((instr & 0xe0000000) == CODING_COLS) { | ||
463 | unsigned int fn = COLS_FN_BITS(instr); | ||
464 | unsigned long val = 0; | ||
465 | if (COLS_L_BITS(instr)) { | ||
466 | get32t_unaligned_check(val, addr); | ||
467 | switch (fn) { | ||
468 | #define ASM_MTF(n) case n: \ | ||
469 | __asm__ __volatile__("MTF %0, F" __stringify(n) \ | ||
470 | : : "r"(val)); \ | ||
471 | break; | ||
472 | ASM_MTF(0); ASM_MTF(1); ASM_MTF(2); ASM_MTF(3); | ||
473 | ASM_MTF(4); ASM_MTF(5); ASM_MTF(6); ASM_MTF(7); | ||
474 | ASM_MTF(8); ASM_MTF(9); ASM_MTF(10); ASM_MTF(11); | ||
475 | ASM_MTF(12); ASM_MTF(13); ASM_MTF(14); ASM_MTF(15); | ||
476 | ASM_MTF(16); ASM_MTF(17); ASM_MTF(18); ASM_MTF(19); | ||
477 | ASM_MTF(20); ASM_MTF(21); ASM_MTF(22); ASM_MTF(23); | ||
478 | ASM_MTF(24); ASM_MTF(25); ASM_MTF(26); ASM_MTF(27); | ||
479 | ASM_MTF(28); ASM_MTF(29); ASM_MTF(30); ASM_MTF(31); | ||
480 | #undef ASM_MTF | ||
481 | } | ||
482 | } else { | ||
483 | switch (fn) { | ||
484 | #define ASM_MFF(n) case n: \ | ||
485 | __asm__ __volatile__("MFF %0, F" __stringify(n) \ | ||
486 | : : "r"(val)); \ | ||
487 | break; | ||
488 | ASM_MFF(0); ASM_MFF(1); ASM_MFF(2); ASM_MFF(3); | ||
489 | ASM_MFF(4); ASM_MFF(5); ASM_MFF(6); ASM_MFF(7); | ||
490 | ASM_MFF(8); ASM_MFF(9); ASM_MFF(10); ASM_MFF(11); | ||
491 | ASM_MFF(12); ASM_MFF(13); ASM_MFF(14); ASM_MFF(15); | ||
492 | ASM_MFF(16); ASM_MFF(17); ASM_MFF(18); ASM_MFF(19); | ||
493 | ASM_MFF(20); ASM_MFF(21); ASM_MFF(22); ASM_MFF(23); | ||
494 | ASM_MFF(24); ASM_MFF(25); ASM_MFF(26); ASM_MFF(27); | ||
495 | ASM_MFF(28); ASM_MFF(29); ASM_MFF(30); ASM_MFF(31); | ||
496 | #undef ASM_MFF | ||
497 | } | ||
498 | put32t_unaligned_check(val, addr); | ||
499 | } | ||
500 | return TYPE_COLS; | ||
501 | } | ||
502 | fault: | ||
503 | return TYPE_FAULT; | ||
504 | #endif | ||
505 | printk(KERN_ERR "Alignment trap: not handling instruction " | ||
506 | "%08lx at [<%08lx>]\n", instr, instrptr); | ||
507 | return 1; | ||
508 | } | ||
509 | |||
510 | /* | ||
511 | * This needs to be done after sysctl_init, otherwise sys/ will be | ||
512 | * overwritten. Actually, this shouldn't be in sys/ at all since | ||
513 | * it isn't a sysctl, and it doesn't contain sysctl information. | ||
514 | */ | ||
515 | static int __init alignment_init(void) | ||
516 | { | ||
517 | hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN, | ||
518 | "alignment exception"); | ||
519 | |||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | fs_initcall(alignment_init); | ||
diff --git a/arch/unicore32/mm/extable.c b/arch/unicore32/mm/extable.c new file mode 100644 index 000000000000..6564180eb285 --- /dev/null +++ b/arch/unicore32/mm/extable.c | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/mm/extable.c | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/uaccess.h> | ||
14 | |||
15 | int fixup_exception(struct pt_regs *regs) | ||
16 | { | ||
17 | const struct exception_table_entry *fixup; | ||
18 | |||
19 | fixup = search_exception_tables(instruction_pointer(regs)); | ||
20 | if (fixup) | ||
21 | regs->UCreg_pc = fixup->fixup; | ||
22 | |||
23 | return fixup != NULL; | ||
24 | } | ||
diff --git a/arch/unicore32/mm/fault.c b/arch/unicore32/mm/fault.c new file mode 100644 index 000000000000..283aa4b50b7a --- /dev/null +++ b/arch/unicore32/mm/fault.c | |||
@@ -0,0 +1,479 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/mm/fault.c | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/signal.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/hardirq.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/kprobes.h> | ||
18 | #include <linux/uaccess.h> | ||
19 | #include <linux/page-flags.h> | ||
20 | #include <linux/sched.h> | ||
21 | #include <linux/io.h> | ||
22 | |||
23 | #include <asm/system.h> | ||
24 | #include <asm/pgtable.h> | ||
25 | #include <asm/tlbflush.h> | ||
26 | |||
27 | /* | ||
28 | * Fault status register encodings. We steal bit 31 for our own purposes. | ||
29 | */ | ||
30 | #define FSR_LNX_PF (1 << 31) | ||
31 | |||
32 | static inline int fsr_fs(unsigned int fsr) | ||
33 | { | ||
34 | /* xyabcde will be abcde+xy */ | ||
35 | return (fsr & 31) + ((fsr & (3 << 5)) >> 5); | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * This is useful to dump out the page tables associated with | ||
40 | * 'addr' in mm 'mm'. | ||
41 | */ | ||
42 | void show_pte(struct mm_struct *mm, unsigned long addr) | ||
43 | { | ||
44 | pgd_t *pgd; | ||
45 | |||
46 | if (!mm) | ||
47 | mm = &init_mm; | ||
48 | |||
49 | printk(KERN_ALERT "pgd = %p\n", mm->pgd); | ||
50 | pgd = pgd_offset(mm, addr); | ||
51 | printk(KERN_ALERT "[%08lx] *pgd=%08lx", addr, pgd_val(*pgd)); | ||
52 | |||
53 | do { | ||
54 | pmd_t *pmd; | ||
55 | pte_t *pte; | ||
56 | |||
57 | if (pgd_none(*pgd)) | ||
58 | break; | ||
59 | |||
60 | if (pgd_bad(*pgd)) { | ||
61 | printk("(bad)"); | ||
62 | break; | ||
63 | } | ||
64 | |||
65 | pmd = pmd_offset((pud_t *) pgd, addr); | ||
66 | if (PTRS_PER_PMD != 1) | ||
67 | printk(", *pmd=%08lx", pmd_val(*pmd)); | ||
68 | |||
69 | if (pmd_none(*pmd)) | ||
70 | break; | ||
71 | |||
72 | if (pmd_bad(*pmd)) { | ||
73 | printk("(bad)"); | ||
74 | break; | ||
75 | } | ||
76 | |||
77 | /* We must not map this if we have highmem enabled */ | ||
78 | if (PageHighMem(pfn_to_page(pmd_val(*pmd) >> PAGE_SHIFT))) | ||
79 | break; | ||
80 | |||
81 | pte = pte_offset_map(pmd, addr); | ||
82 | printk(", *pte=%08lx", pte_val(*pte)); | ||
83 | pte_unmap(pte); | ||
84 | } while (0); | ||
85 | |||
86 | printk("\n"); | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * Oops. The kernel tried to access some page that wasn't present. | ||
91 | */ | ||
92 | static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr, | ||
93 | unsigned int fsr, struct pt_regs *regs) | ||
94 | { | ||
95 | /* | ||
96 | * Are we prepared to handle this kernel fault? | ||
97 | */ | ||
98 | if (fixup_exception(regs)) | ||
99 | return; | ||
100 | |||
101 | /* | ||
102 | * No handler, we'll have to terminate things with extreme prejudice. | ||
103 | */ | ||
104 | bust_spinlocks(1); | ||
105 | printk(KERN_ALERT | ||
106 | "Unable to handle kernel %s at virtual address %08lx\n", | ||
107 | (addr < PAGE_SIZE) ? "NULL pointer dereference" : | ||
108 | "paging request", addr); | ||
109 | |||
110 | show_pte(mm, addr); | ||
111 | die("Oops", regs, fsr); | ||
112 | bust_spinlocks(0); | ||
113 | do_exit(SIGKILL); | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * Something tried to access memory that isn't in our memory map.. | ||
118 | * User mode accesses just cause a SIGSEGV | ||
119 | */ | ||
120 | static void __do_user_fault(struct task_struct *tsk, unsigned long addr, | ||
121 | unsigned int fsr, unsigned int sig, int code, | ||
122 | struct pt_regs *regs) | ||
123 | { | ||
124 | struct siginfo si; | ||
125 | |||
126 | tsk->thread.address = addr; | ||
127 | tsk->thread.error_code = fsr; | ||
128 | tsk->thread.trap_no = 14; | ||
129 | si.si_signo = sig; | ||
130 | si.si_errno = 0; | ||
131 | si.si_code = code; | ||
132 | si.si_addr = (void __user *)addr; | ||
133 | force_sig_info(sig, &si, tsk); | ||
134 | } | ||
135 | |||
136 | void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | ||
137 | { | ||
138 | struct task_struct *tsk = current; | ||
139 | struct mm_struct *mm = tsk->active_mm; | ||
140 | |||
141 | /* | ||
142 | * If we are in kernel mode at this point, we | ||
143 | * have no context to handle this fault with. | ||
144 | */ | ||
145 | if (user_mode(regs)) | ||
146 | __do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs); | ||
147 | else | ||
148 | __do_kernel_fault(mm, addr, fsr, regs); | ||
149 | } | ||
150 | |||
151 | #define VM_FAULT_BADMAP 0x010000 | ||
152 | #define VM_FAULT_BADACCESS 0x020000 | ||
153 | |||
154 | /* | ||
155 | * Check that the permissions on the VMA allow for the fault which occurred. | ||
156 | * If we encountered a write fault, we must have write permission, otherwise | ||
157 | * we allow any permission. | ||
158 | */ | ||
159 | static inline bool access_error(unsigned int fsr, struct vm_area_struct *vma) | ||
160 | { | ||
161 | unsigned int mask = VM_READ | VM_WRITE | VM_EXEC; | ||
162 | |||
163 | if (!(fsr ^ 0x12)) /* write? */ | ||
164 | mask = VM_WRITE; | ||
165 | if (fsr & FSR_LNX_PF) | ||
166 | mask = VM_EXEC; | ||
167 | |||
168 | return vma->vm_flags & mask ? false : true; | ||
169 | } | ||
170 | |||
171 | static int __do_pf(struct mm_struct *mm, unsigned long addr, unsigned int fsr, | ||
172 | struct task_struct *tsk) | ||
173 | { | ||
174 | struct vm_area_struct *vma; | ||
175 | int fault; | ||
176 | |||
177 | vma = find_vma(mm, addr); | ||
178 | fault = VM_FAULT_BADMAP; | ||
179 | if (unlikely(!vma)) | ||
180 | goto out; | ||
181 | if (unlikely(vma->vm_start > addr)) | ||
182 | goto check_stack; | ||
183 | |||
184 | /* | ||
185 | * Ok, we have a good vm_area for this | ||
186 | * memory access, so we can handle it. | ||
187 | */ | ||
188 | good_area: | ||
189 | if (access_error(fsr, vma)) { | ||
190 | fault = VM_FAULT_BADACCESS; | ||
191 | goto out; | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * If for any reason at all we couldn't handle the fault, make | ||
196 | * sure we exit gracefully rather than endlessly redo the fault. | ||
197 | */ | ||
198 | fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, | ||
199 | (!(fsr ^ 0x12)) ? FAULT_FLAG_WRITE : 0); | ||
200 | if (unlikely(fault & VM_FAULT_ERROR)) | ||
201 | return fault; | ||
202 | if (fault & VM_FAULT_MAJOR) | ||
203 | tsk->maj_flt++; | ||
204 | else | ||
205 | tsk->min_flt++; | ||
206 | return fault; | ||
207 | |||
208 | check_stack: | ||
209 | if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr)) | ||
210 | goto good_area; | ||
211 | out: | ||
212 | return fault; | ||
213 | } | ||
214 | |||
215 | static int do_pf(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | ||
216 | { | ||
217 | struct task_struct *tsk; | ||
218 | struct mm_struct *mm; | ||
219 | int fault, sig, code; | ||
220 | |||
221 | tsk = current; | ||
222 | mm = tsk->mm; | ||
223 | |||
224 | /* | ||
225 | * If we're in an interrupt or have no user | ||
226 | * context, we must not take the fault.. | ||
227 | */ | ||
228 | if (in_atomic() || !mm) | ||
229 | goto no_context; | ||
230 | |||
231 | /* | ||
232 | * As per x86, we may deadlock here. However, since the kernel only | ||
233 | * validly references user space from well defined areas of the code, | ||
234 | * we can bug out early if this is from code which shouldn't. | ||
235 | */ | ||
236 | if (!down_read_trylock(&mm->mmap_sem)) { | ||
237 | if (!user_mode(regs) | ||
238 | && !search_exception_tables(regs->UCreg_pc)) | ||
239 | goto no_context; | ||
240 | down_read(&mm->mmap_sem); | ||
241 | } else { | ||
242 | /* | ||
243 | * The above down_read_trylock() might have succeeded in | ||
244 | * which case, we'll have missed the might_sleep() from | ||
245 | * down_read() | ||
246 | */ | ||
247 | might_sleep(); | ||
248 | #ifdef CONFIG_DEBUG_VM | ||
249 | if (!user_mode(regs) && | ||
250 | !search_exception_tables(regs->UCreg_pc)) | ||
251 | goto no_context; | ||
252 | #endif | ||
253 | } | ||
254 | |||
255 | fault = __do_pf(mm, addr, fsr, tsk); | ||
256 | up_read(&mm->mmap_sem); | ||
257 | |||
258 | /* | ||
259 | * Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR | ||
260 | */ | ||
261 | if (likely(!(fault & | ||
262 | (VM_FAULT_ERROR | VM_FAULT_BADMAP | VM_FAULT_BADACCESS)))) | ||
263 | return 0; | ||
264 | |||
265 | if (fault & VM_FAULT_OOM) { | ||
266 | /* | ||
267 | * We ran out of memory, call the OOM killer, and return to | ||
268 | * userspace (which will retry the fault, or kill us if we | ||
269 | * got oom-killed) | ||
270 | */ | ||
271 | pagefault_out_of_memory(); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | /* | ||
276 | * If we are in kernel mode at this point, we | ||
277 | * have no context to handle this fault with. | ||
278 | */ | ||
279 | if (!user_mode(regs)) | ||
280 | goto no_context; | ||
281 | |||
282 | if (fault & VM_FAULT_SIGBUS) { | ||
283 | /* | ||
284 | * We had some memory, but were unable to | ||
285 | * successfully fix up this page fault. | ||
286 | */ | ||
287 | sig = SIGBUS; | ||
288 | code = BUS_ADRERR; | ||
289 | } else { | ||
290 | /* | ||
291 | * Something tried to access memory that | ||
292 | * isn't in our memory map.. | ||
293 | */ | ||
294 | sig = SIGSEGV; | ||
295 | code = fault == VM_FAULT_BADACCESS ? SEGV_ACCERR : SEGV_MAPERR; | ||
296 | } | ||
297 | |||
298 | __do_user_fault(tsk, addr, fsr, sig, code, regs); | ||
299 | return 0; | ||
300 | |||
301 | no_context: | ||
302 | __do_kernel_fault(mm, addr, fsr, regs); | ||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * First Level Translation Fault Handler | ||
308 | * | ||
309 | * We enter here because the first level page table doesn't contain | ||
310 | * a valid entry for the address. | ||
311 | * | ||
312 | * If the address is in kernel space (>= TASK_SIZE), then we are | ||
313 | * probably faulting in the vmalloc() area. | ||
314 | * | ||
315 | * If the init_task's first level page tables contains the relevant | ||
316 | * entry, we copy the it to this task. If not, we send the process | ||
317 | * a signal, fixup the exception, or oops the kernel. | ||
318 | * | ||
319 | * NOTE! We MUST NOT take any locks for this case. We may be in an | ||
320 | * interrupt or a critical region, and should only copy the information | ||
321 | * from the master page table, nothing more. | ||
322 | */ | ||
323 | static int do_ifault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | ||
324 | { | ||
325 | unsigned int index; | ||
326 | pgd_t *pgd, *pgd_k; | ||
327 | pmd_t *pmd, *pmd_k; | ||
328 | |||
329 | if (addr < TASK_SIZE) | ||
330 | return do_pf(addr, fsr, regs); | ||
331 | |||
332 | if (user_mode(regs)) | ||
333 | goto bad_area; | ||
334 | |||
335 | index = pgd_index(addr); | ||
336 | |||
337 | pgd = cpu_get_pgd() + index; | ||
338 | pgd_k = init_mm.pgd + index; | ||
339 | |||
340 | if (pgd_none(*pgd_k)) | ||
341 | goto bad_area; | ||
342 | |||
343 | pmd_k = pmd_offset((pud_t *) pgd_k, addr); | ||
344 | pmd = pmd_offset((pud_t *) pgd, addr); | ||
345 | |||
346 | if (pmd_none(*pmd_k)) | ||
347 | goto bad_area; | ||
348 | |||
349 | set_pmd(pmd, *pmd_k); | ||
350 | flush_pmd_entry(pmd); | ||
351 | return 0; | ||
352 | |||
353 | bad_area: | ||
354 | do_bad_area(addr, fsr, regs); | ||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | /* | ||
359 | * This abort handler always returns "fault". | ||
360 | */ | ||
361 | static int do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | ||
362 | { | ||
363 | return 1; | ||
364 | } | ||
365 | |||
366 | static int do_good(unsigned long addr, unsigned int fsr, struct pt_regs *regs) | ||
367 | { | ||
368 | unsigned int res1, res2; | ||
369 | |||
370 | printk("dabt exception but no error!\n"); | ||
371 | |||
372 | __asm__ __volatile__( | ||
373 | "mff %0,f0\n" | ||
374 | "mff %1,f1\n" | ||
375 | : "=r"(res1), "=r"(res2) | ||
376 | : | ||
377 | : "memory"); | ||
378 | |||
379 | printk(KERN_EMERG "r0 :%08x r1 :%08x\n", res1, res2); | ||
380 | panic("shut up\n"); | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | static struct fsr_info { | ||
385 | int (*fn) (unsigned long addr, unsigned int fsr, struct pt_regs *regs); | ||
386 | int sig; | ||
387 | int code; | ||
388 | const char *name; | ||
389 | } fsr_info[] = { | ||
390 | /* | ||
391 | * The following are the standard Unicore-I and UniCore-II aborts. | ||
392 | */ | ||
393 | { do_good, SIGBUS, 0, "no error" }, | ||
394 | { do_bad, SIGBUS, BUS_ADRALN, "alignment exception" }, | ||
395 | { do_bad, SIGBUS, BUS_OBJERR, "external exception" }, | ||
396 | { do_bad, SIGBUS, 0, "burst operation" }, | ||
397 | { do_bad, SIGBUS, 0, "unknown 00100" }, | ||
398 | { do_ifault, SIGSEGV, SEGV_MAPERR, "2nd level pt non-exist"}, | ||
399 | { do_bad, SIGBUS, 0, "2nd lvl large pt non-exist" }, | ||
400 | { do_bad, SIGBUS, 0, "invalid pte" }, | ||
401 | { do_pf, SIGSEGV, SEGV_MAPERR, "page miss" }, | ||
402 | { do_bad, SIGBUS, 0, "middle page miss" }, | ||
403 | { do_bad, SIGBUS, 0, "large page miss" }, | ||
404 | { do_pf, SIGSEGV, SEGV_MAPERR, "super page (section) miss" }, | ||
405 | { do_bad, SIGBUS, 0, "unknown 01100" }, | ||
406 | { do_bad, SIGBUS, 0, "unknown 01101" }, | ||
407 | { do_bad, SIGBUS, 0, "unknown 01110" }, | ||
408 | { do_bad, SIGBUS, 0, "unknown 01111" }, | ||
409 | { do_bad, SIGBUS, 0, "addr: up 3G or IO" }, | ||
410 | { do_pf, SIGSEGV, SEGV_ACCERR, "read unreadable addr" }, | ||
411 | { do_pf, SIGSEGV, SEGV_ACCERR, "write unwriteable addr"}, | ||
412 | { do_pf, SIGSEGV, SEGV_ACCERR, "exec unexecutable addr"}, | ||
413 | { do_bad, SIGBUS, 0, "unknown 10100" }, | ||
414 | { do_bad, SIGBUS, 0, "unknown 10101" }, | ||
415 | { do_bad, SIGBUS, 0, "unknown 10110" }, | ||
416 | { do_bad, SIGBUS, 0, "unknown 10111" }, | ||
417 | { do_bad, SIGBUS, 0, "unknown 11000" }, | ||
418 | { do_bad, SIGBUS, 0, "unknown 11001" }, | ||
419 | { do_bad, SIGBUS, 0, "unknown 11010" }, | ||
420 | { do_bad, SIGBUS, 0, "unknown 11011" }, | ||
421 | { do_bad, SIGBUS, 0, "unknown 11100" }, | ||
422 | { do_bad, SIGBUS, 0, "unknown 11101" }, | ||
423 | { do_bad, SIGBUS, 0, "unknown 11110" }, | ||
424 | { do_bad, SIGBUS, 0, "unknown 11111" } | ||
425 | }; | ||
426 | |||
427 | void __init hook_fault_code(int nr, | ||
428 | int (*fn) (unsigned long, unsigned int, struct pt_regs *), | ||
429 | int sig, int code, const char *name) | ||
430 | { | ||
431 | if (nr < 0 || nr >= ARRAY_SIZE(fsr_info)) | ||
432 | BUG(); | ||
433 | |||
434 | fsr_info[nr].fn = fn; | ||
435 | fsr_info[nr].sig = sig; | ||
436 | fsr_info[nr].code = code; | ||
437 | fsr_info[nr].name = name; | ||
438 | } | ||
439 | |||
440 | /* | ||
441 | * Dispatch a data abort to the relevant handler. | ||
442 | */ | ||
443 | asmlinkage void do_DataAbort(unsigned long addr, unsigned int fsr, | ||
444 | struct pt_regs *regs) | ||
445 | { | ||
446 | const struct fsr_info *inf = fsr_info + fsr_fs(fsr); | ||
447 | struct siginfo info; | ||
448 | |||
449 | if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs)) | ||
450 | return; | ||
451 | |||
452 | printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lx\n", | ||
453 | inf->name, fsr, addr); | ||
454 | |||
455 | info.si_signo = inf->sig; | ||
456 | info.si_errno = 0; | ||
457 | info.si_code = inf->code; | ||
458 | info.si_addr = (void __user *)addr; | ||
459 | uc32_notify_die("", regs, &info, fsr, 0); | ||
460 | } | ||
461 | |||
462 | asmlinkage void do_PrefetchAbort(unsigned long addr, | ||
463 | unsigned int ifsr, struct pt_regs *regs) | ||
464 | { | ||
465 | const struct fsr_info *inf = fsr_info + fsr_fs(ifsr); | ||
466 | struct siginfo info; | ||
467 | |||
468 | if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs)) | ||
469 | return; | ||
470 | |||
471 | printk(KERN_ALERT "Unhandled prefetch abort: %s (0x%03x) at 0x%08lx\n", | ||
472 | inf->name, ifsr, addr); | ||
473 | |||
474 | info.si_signo = inf->sig; | ||
475 | info.si_errno = 0; | ||
476 | info.si_code = inf->code; | ||
477 | info.si_addr = (void __user *)addr; | ||
478 | uc32_notify_die("", regs, &info, ifsr, 0); | ||
479 | } | ||
diff --git a/arch/unicore32/mm/mmu.c b/arch/unicore32/mm/mmu.c new file mode 100644 index 000000000000..7bf3d588631f --- /dev/null +++ b/arch/unicore32/mm/mmu.c | |||
@@ -0,0 +1,533 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/mm/mmu.c | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/mman.h> | ||
17 | #include <linux/nodemask.h> | ||
18 | #include <linux/memblock.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/bootmem.h> | ||
21 | #include <linux/io.h> | ||
22 | |||
23 | #include <asm/cputype.h> | ||
24 | #include <asm/sections.h> | ||
25 | #include <asm/setup.h> | ||
26 | #include <asm/sizes.h> | ||
27 | #include <asm/tlb.h> | ||
28 | |||
29 | #include <mach/map.h> | ||
30 | |||
31 | #include "mm.h" | ||
32 | |||
33 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); | ||
34 | |||
35 | /* | ||
36 | * empty_zero_page is a special page that is used for | ||
37 | * zero-initialized data and COW. | ||
38 | */ | ||
39 | struct page *empty_zero_page; | ||
40 | EXPORT_SYMBOL(empty_zero_page); | ||
41 | |||
42 | /* | ||
43 | * The pmd table for the upper-most set of pages. | ||
44 | */ | ||
45 | pmd_t *top_pmd; | ||
46 | |||
47 | pgprot_t pgprot_user; | ||
48 | EXPORT_SYMBOL(pgprot_user); | ||
49 | |||
50 | pgprot_t pgprot_kernel; | ||
51 | EXPORT_SYMBOL(pgprot_kernel); | ||
52 | |||
53 | static int __init noalign_setup(char *__unused) | ||
54 | { | ||
55 | cr_alignment &= ~CR_A; | ||
56 | cr_no_alignment &= ~CR_A; | ||
57 | set_cr(cr_alignment); | ||
58 | return 1; | ||
59 | } | ||
60 | __setup("noalign", noalign_setup); | ||
61 | |||
62 | void adjust_cr(unsigned long mask, unsigned long set) | ||
63 | { | ||
64 | unsigned long flags; | ||
65 | |||
66 | mask &= ~CR_A; | ||
67 | |||
68 | set &= mask; | ||
69 | |||
70 | local_irq_save(flags); | ||
71 | |||
72 | cr_no_alignment = (cr_no_alignment & ~mask) | set; | ||
73 | cr_alignment = (cr_alignment & ~mask) | set; | ||
74 | |||
75 | set_cr((get_cr() & ~mask) | set); | ||
76 | |||
77 | local_irq_restore(flags); | ||
78 | } | ||
79 | |||
80 | struct map_desc { | ||
81 | unsigned long virtual; | ||
82 | unsigned long pfn; | ||
83 | unsigned long length; | ||
84 | unsigned int type; | ||
85 | }; | ||
86 | |||
87 | #define PROT_PTE_DEVICE (PTE_PRESENT | PTE_YOUNG | \ | ||
88 | PTE_DIRTY | PTE_READ | PTE_WRITE) | ||
89 | #define PROT_SECT_DEVICE (PMD_TYPE_SECT | PMD_PRESENT | \ | ||
90 | PMD_SECT_READ | PMD_SECT_WRITE) | ||
91 | |||
92 | static struct mem_type mem_types[] = { | ||
93 | [MT_DEVICE] = { /* Strongly ordered */ | ||
94 | .prot_pte = PROT_PTE_DEVICE, | ||
95 | .prot_l1 = PMD_TYPE_TABLE | PMD_PRESENT, | ||
96 | .prot_sect = PROT_SECT_DEVICE, | ||
97 | }, | ||
98 | /* | ||
99 | * MT_KUSER: pte for vecpage -- cacheable, | ||
100 | * and sect for unigfx mmap -- noncacheable | ||
101 | */ | ||
102 | [MT_KUSER] = { | ||
103 | .prot_pte = PTE_PRESENT | PTE_YOUNG | PTE_DIRTY | | ||
104 | PTE_CACHEABLE | PTE_READ | PTE_EXEC, | ||
105 | .prot_l1 = PMD_TYPE_TABLE | PMD_PRESENT, | ||
106 | .prot_sect = PROT_SECT_DEVICE, | ||
107 | }, | ||
108 | [MT_HIGH_VECTORS] = { | ||
109 | .prot_pte = PTE_PRESENT | PTE_YOUNG | PTE_DIRTY | | ||
110 | PTE_CACHEABLE | PTE_READ | PTE_WRITE | | ||
111 | PTE_EXEC, | ||
112 | .prot_l1 = PMD_TYPE_TABLE | PMD_PRESENT, | ||
113 | }, | ||
114 | [MT_MEMORY] = { | ||
115 | .prot_pte = PTE_PRESENT | PTE_YOUNG | PTE_DIRTY | | ||
116 | PTE_WRITE | PTE_EXEC, | ||
117 | .prot_l1 = PMD_TYPE_TABLE | PMD_PRESENT, | ||
118 | .prot_sect = PMD_TYPE_SECT | PMD_PRESENT | PMD_SECT_CACHEABLE | | ||
119 | PMD_SECT_READ | PMD_SECT_WRITE | PMD_SECT_EXEC, | ||
120 | }, | ||
121 | [MT_ROM] = { | ||
122 | .prot_sect = PMD_TYPE_SECT | PMD_PRESENT | PMD_SECT_CACHEABLE | | ||
123 | PMD_SECT_READ, | ||
124 | }, | ||
125 | }; | ||
126 | |||
127 | const struct mem_type *get_mem_type(unsigned int type) | ||
128 | { | ||
129 | return type < ARRAY_SIZE(mem_types) ? &mem_types[type] : NULL; | ||
130 | } | ||
131 | EXPORT_SYMBOL(get_mem_type); | ||
132 | |||
133 | /* | ||
134 | * Adjust the PMD section entries according to the CPU in use. | ||
135 | */ | ||
136 | static void __init build_mem_type_table(void) | ||
137 | { | ||
138 | pgprot_user = __pgprot(PTE_PRESENT | PTE_YOUNG | PTE_CACHEABLE); | ||
139 | pgprot_kernel = __pgprot(PTE_PRESENT | PTE_YOUNG | | ||
140 | PTE_DIRTY | PTE_READ | PTE_WRITE | | ||
141 | PTE_EXEC | PTE_CACHEABLE); | ||
142 | } | ||
143 | |||
144 | #define vectors_base() (vectors_high() ? 0xffff0000 : 0) | ||
145 | |||
146 | static void __init *early_alloc(unsigned long sz) | ||
147 | { | ||
148 | void *ptr = __va(memblock_alloc(sz, sz)); | ||
149 | memset(ptr, 0, sz); | ||
150 | return ptr; | ||
151 | } | ||
152 | |||
153 | static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, | ||
154 | unsigned long prot) | ||
155 | { | ||
156 | if (pmd_none(*pmd)) { | ||
157 | pte_t *pte = early_alloc(PTRS_PER_PTE * sizeof(pte_t)); | ||
158 | __pmd_populate(pmd, __pa(pte) | prot); | ||
159 | } | ||
160 | BUG_ON(pmd_bad(*pmd)); | ||
161 | return pte_offset_kernel(pmd, addr); | ||
162 | } | ||
163 | |||
164 | static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr, | ||
165 | unsigned long end, unsigned long pfn, | ||
166 | const struct mem_type *type) | ||
167 | { | ||
168 | pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1); | ||
169 | do { | ||
170 | set_pte(pte, pfn_pte(pfn, __pgprot(type->prot_pte))); | ||
171 | pfn++; | ||
172 | } while (pte++, addr += PAGE_SIZE, addr != end); | ||
173 | } | ||
174 | |||
175 | static void __init alloc_init_section(pgd_t *pgd, unsigned long addr, | ||
176 | unsigned long end, unsigned long phys, | ||
177 | const struct mem_type *type) | ||
178 | { | ||
179 | pmd_t *pmd = pmd_offset((pud_t *)pgd, addr); | ||
180 | |||
181 | /* | ||
182 | * Try a section mapping - end, addr and phys must all be aligned | ||
183 | * to a section boundary. | ||
184 | */ | ||
185 | if (((addr | end | phys) & ~SECTION_MASK) == 0) { | ||
186 | pmd_t *p = pmd; | ||
187 | |||
188 | do { | ||
189 | set_pmd(pmd, __pmd(phys | type->prot_sect)); | ||
190 | phys += SECTION_SIZE; | ||
191 | } while (pmd++, addr += SECTION_SIZE, addr != end); | ||
192 | |||
193 | flush_pmd_entry(p); | ||
194 | } else { | ||
195 | /* | ||
196 | * No need to loop; pte's aren't interested in the | ||
197 | * individual L1 entries. | ||
198 | */ | ||
199 | alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | /* | ||
204 | * Create the page directory entries and any necessary | ||
205 | * page tables for the mapping specified by `md'. We | ||
206 | * are able to cope here with varying sizes and address | ||
207 | * offsets, and we take full advantage of sections. | ||
208 | */ | ||
209 | static void __init create_mapping(struct map_desc *md) | ||
210 | { | ||
211 | unsigned long phys, addr, length, end; | ||
212 | const struct mem_type *type; | ||
213 | pgd_t *pgd; | ||
214 | |||
215 | if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) { | ||
216 | printk(KERN_WARNING "BUG: not creating mapping for " | ||
217 | "0x%08llx at 0x%08lx in user region\n", | ||
218 | __pfn_to_phys((u64)md->pfn), md->virtual); | ||
219 | return; | ||
220 | } | ||
221 | |||
222 | if ((md->type == MT_DEVICE || md->type == MT_ROM) && | ||
223 | md->virtual >= PAGE_OFFSET && md->virtual < VMALLOC_END) { | ||
224 | printk(KERN_WARNING "BUG: mapping for 0x%08llx at 0x%08lx " | ||
225 | "overlaps vmalloc space\n", | ||
226 | __pfn_to_phys((u64)md->pfn), md->virtual); | ||
227 | } | ||
228 | |||
229 | type = &mem_types[md->type]; | ||
230 | |||
231 | addr = md->virtual & PAGE_MASK; | ||
232 | phys = (unsigned long)__pfn_to_phys(md->pfn); | ||
233 | length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK)); | ||
234 | |||
235 | if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) { | ||
236 | printk(KERN_WARNING "BUG: map for 0x%08lx at 0x%08lx can not " | ||
237 | "be mapped using pages, ignoring.\n", | ||
238 | __pfn_to_phys(md->pfn), addr); | ||
239 | return; | ||
240 | } | ||
241 | |||
242 | pgd = pgd_offset_k(addr); | ||
243 | end = addr + length; | ||
244 | do { | ||
245 | unsigned long next = pgd_addr_end(addr, end); | ||
246 | |||
247 | alloc_init_section(pgd, addr, next, phys, type); | ||
248 | |||
249 | phys += next - addr; | ||
250 | addr = next; | ||
251 | } while (pgd++, addr != end); | ||
252 | } | ||
253 | |||
254 | static void * __initdata vmalloc_min = (void *)(VMALLOC_END - SZ_128M); | ||
255 | |||
256 | /* | ||
257 | * vmalloc=size forces the vmalloc area to be exactly 'size' | ||
258 | * bytes. This can be used to increase (or decrease) the vmalloc | ||
259 | * area - the default is 128m. | ||
260 | */ | ||
261 | static int __init early_vmalloc(char *arg) | ||
262 | { | ||
263 | unsigned long vmalloc_reserve = memparse(arg, NULL); | ||
264 | |||
265 | if (vmalloc_reserve < SZ_16M) { | ||
266 | vmalloc_reserve = SZ_16M; | ||
267 | printk(KERN_WARNING | ||
268 | "vmalloc area too small, limiting to %luMB\n", | ||
269 | vmalloc_reserve >> 20); | ||
270 | } | ||
271 | |||
272 | if (vmalloc_reserve > VMALLOC_END - (PAGE_OFFSET + SZ_32M)) { | ||
273 | vmalloc_reserve = VMALLOC_END - (PAGE_OFFSET + SZ_32M); | ||
274 | printk(KERN_WARNING | ||
275 | "vmalloc area is too big, limiting to %luMB\n", | ||
276 | vmalloc_reserve >> 20); | ||
277 | } | ||
278 | |||
279 | vmalloc_min = (void *)(VMALLOC_END - vmalloc_reserve); | ||
280 | return 0; | ||
281 | } | ||
282 | early_param("vmalloc", early_vmalloc); | ||
283 | |||
284 | static phys_addr_t lowmem_limit __initdata = SZ_1G; | ||
285 | |||
286 | static void __init sanity_check_meminfo(void) | ||
287 | { | ||
288 | int i, j; | ||
289 | |||
290 | lowmem_limit = __pa(vmalloc_min - 1) + 1; | ||
291 | memblock_set_current_limit(lowmem_limit); | ||
292 | |||
293 | for (i = 0, j = 0; i < meminfo.nr_banks; i++) { | ||
294 | struct membank *bank = &meminfo.bank[j]; | ||
295 | *bank = meminfo.bank[i]; | ||
296 | j++; | ||
297 | } | ||
298 | meminfo.nr_banks = j; | ||
299 | } | ||
300 | |||
301 | static inline void prepare_page_table(void) | ||
302 | { | ||
303 | unsigned long addr; | ||
304 | phys_addr_t end; | ||
305 | |||
306 | /* | ||
307 | * Clear out all the mappings below the kernel image. | ||
308 | */ | ||
309 | for (addr = 0; addr < MODULES_VADDR; addr += PGDIR_SIZE) | ||
310 | pmd_clear(pmd_off_k(addr)); | ||
311 | |||
312 | for ( ; addr < PAGE_OFFSET; addr += PGDIR_SIZE) | ||
313 | pmd_clear(pmd_off_k(addr)); | ||
314 | |||
315 | /* | ||
316 | * Find the end of the first block of lowmem. | ||
317 | */ | ||
318 | end = memblock.memory.regions[0].base + memblock.memory.regions[0].size; | ||
319 | if (end >= lowmem_limit) | ||
320 | end = lowmem_limit; | ||
321 | |||
322 | /* | ||
323 | * Clear out all the kernel space mappings, except for the first | ||
324 | * memory bank, up to the end of the vmalloc region. | ||
325 | */ | ||
326 | for (addr = __phys_to_virt(end); | ||
327 | addr < VMALLOC_END; addr += PGDIR_SIZE) | ||
328 | pmd_clear(pmd_off_k(addr)); | ||
329 | } | ||
330 | |||
331 | /* | ||
332 | * Reserve the special regions of memory | ||
333 | */ | ||
334 | void __init uc32_mm_memblock_reserve(void) | ||
335 | { | ||
336 | /* | ||
337 | * Reserve the page tables. These are already in use, | ||
338 | * and can only be in node 0. | ||
339 | */ | ||
340 | memblock_reserve(__pa(swapper_pg_dir), PTRS_PER_PGD * sizeof(pgd_t)); | ||
341 | |||
342 | #ifdef CONFIG_PUV3_UNIGFX | ||
343 | /* | ||
344 | * These should likewise go elsewhere. They pre-reserve the | ||
345 | * screen/video memory region at the 48M~64M of main system memory. | ||
346 | */ | ||
347 | memblock_reserve(PKUNITY_UNIGFX_MMAP_BASE, PKUNITY_UNIGFX_MMAP_SIZE); | ||
348 | memblock_reserve(PKUNITY_UVC_MMAP_BASE, PKUNITY_UVC_MMAP_SIZE); | ||
349 | #endif | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * Set up device the mappings. Since we clear out the page tables for all | ||
354 | * mappings above VMALLOC_END, we will remove any debug device mappings. | ||
355 | * This means you have to be careful how you debug this function, or any | ||
356 | * called function. This means you can't use any function or debugging | ||
357 | * method which may touch any device, otherwise the kernel _will_ crash. | ||
358 | */ | ||
359 | static void __init devicemaps_init(void) | ||
360 | { | ||
361 | struct map_desc map; | ||
362 | unsigned long addr; | ||
363 | void *vectors; | ||
364 | |||
365 | /* | ||
366 | * Allocate the vector page early. | ||
367 | */ | ||
368 | vectors = early_alloc(PAGE_SIZE); | ||
369 | |||
370 | for (addr = VMALLOC_END; addr; addr += PGDIR_SIZE) | ||
371 | pmd_clear(pmd_off_k(addr)); | ||
372 | |||
373 | /* | ||
374 | * Create a mapping for UniGFX VRAM | ||
375 | */ | ||
376 | #ifdef CONFIG_PUV3_UNIGFX | ||
377 | map.pfn = __phys_to_pfn(PKUNITY_UNIGFX_MMAP_BASE); | ||
378 | map.virtual = KUSER_UNIGFX_BASE; | ||
379 | map.length = PKUNITY_UNIGFX_MMAP_SIZE; | ||
380 | map.type = MT_KUSER; | ||
381 | create_mapping(&map); | ||
382 | #endif | ||
383 | |||
384 | /* | ||
385 | * Create a mapping for the machine vectors at the high-vectors | ||
386 | * location (0xffff0000). If we aren't using high-vectors, also | ||
387 | * create a mapping at the low-vectors virtual address. | ||
388 | */ | ||
389 | map.pfn = __phys_to_pfn(virt_to_phys(vectors)); | ||
390 | map.virtual = VECTORS_BASE; | ||
391 | map.length = PAGE_SIZE; | ||
392 | map.type = MT_HIGH_VECTORS; | ||
393 | create_mapping(&map); | ||
394 | |||
395 | /* | ||
396 | * Create a mapping for the kuser page at the special | ||
397 | * location (0xbfff0000) to the same vectors location. | ||
398 | */ | ||
399 | map.pfn = __phys_to_pfn(virt_to_phys(vectors)); | ||
400 | map.virtual = KUSER_VECPAGE_BASE; | ||
401 | map.length = PAGE_SIZE; | ||
402 | map.type = MT_KUSER; | ||
403 | create_mapping(&map); | ||
404 | |||
405 | /* | ||
406 | * Finally flush the caches and tlb to ensure that we're in a | ||
407 | * consistent state wrt the writebuffer. This also ensures that | ||
408 | * any write-allocated cache lines in the vector page are written | ||
409 | * back. After this point, we can start to touch devices again. | ||
410 | */ | ||
411 | local_flush_tlb_all(); | ||
412 | flush_cache_all(); | ||
413 | } | ||
414 | |||
415 | static void __init map_lowmem(void) | ||
416 | { | ||
417 | struct memblock_region *reg; | ||
418 | |||
419 | /* Map all the lowmem memory banks. */ | ||
420 | for_each_memblock(memory, reg) { | ||
421 | phys_addr_t start = reg->base; | ||
422 | phys_addr_t end = start + reg->size; | ||
423 | struct map_desc map; | ||
424 | |||
425 | if (end > lowmem_limit) | ||
426 | end = lowmem_limit; | ||
427 | if (start >= end) | ||
428 | break; | ||
429 | |||
430 | map.pfn = __phys_to_pfn(start); | ||
431 | map.virtual = __phys_to_virt(start); | ||
432 | map.length = end - start; | ||
433 | map.type = MT_MEMORY; | ||
434 | |||
435 | create_mapping(&map); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | /* | ||
440 | * paging_init() sets up the page tables, initialises the zone memory | ||
441 | * maps, and sets up the zero page, bad page and bad page tables. | ||
442 | */ | ||
443 | void __init paging_init(void) | ||
444 | { | ||
445 | void *zero_page; | ||
446 | |||
447 | build_mem_type_table(); | ||
448 | sanity_check_meminfo(); | ||
449 | prepare_page_table(); | ||
450 | map_lowmem(); | ||
451 | devicemaps_init(); | ||
452 | |||
453 | top_pmd = pmd_off_k(0xffff0000); | ||
454 | |||
455 | /* allocate the zero page. */ | ||
456 | zero_page = early_alloc(PAGE_SIZE); | ||
457 | |||
458 | bootmem_init(); | ||
459 | |||
460 | empty_zero_page = virt_to_page(zero_page); | ||
461 | __flush_dcache_page(NULL, empty_zero_page); | ||
462 | } | ||
463 | |||
464 | /* | ||
465 | * In order to soft-boot, we need to insert a 1:1 mapping in place of | ||
466 | * the user-mode pages. This will then ensure that we have predictable | ||
467 | * results when turning the mmu off | ||
468 | */ | ||
469 | void setup_mm_for_reboot(char mode) | ||
470 | { | ||
471 | unsigned long base_pmdval; | ||
472 | pgd_t *pgd; | ||
473 | int i; | ||
474 | |||
475 | /* | ||
476 | * We need to access to user-mode page tables here. For kernel threads | ||
477 | * we don't have any user-mode mappings so we use the context that we | ||
478 | * "borrowed". | ||
479 | */ | ||
480 | pgd = current->active_mm->pgd; | ||
481 | |||
482 | base_pmdval = PMD_SECT_WRITE | PMD_SECT_READ | PMD_TYPE_SECT; | ||
483 | |||
484 | for (i = 0; i < FIRST_USER_PGD_NR + USER_PTRS_PER_PGD; i++, pgd++) { | ||
485 | unsigned long pmdval = (i << PGDIR_SHIFT) | base_pmdval; | ||
486 | pmd_t *pmd; | ||
487 | |||
488 | pmd = pmd_off(pgd, i << PGDIR_SHIFT); | ||
489 | set_pmd(pmd, __pmd(pmdval)); | ||
490 | flush_pmd_entry(pmd); | ||
491 | } | ||
492 | |||
493 | local_flush_tlb_all(); | ||
494 | } | ||
495 | |||
496 | /* | ||
497 | * Take care of architecture specific things when placing a new PTE into | ||
498 | * a page table, or changing an existing PTE. Basically, there are two | ||
499 | * things that we need to take care of: | ||
500 | * | ||
501 | * 1. If PG_dcache_clean is not set for the page, we need to ensure | ||
502 | * that any cache entries for the kernels virtual memory | ||
503 | * range are written back to the page. | ||
504 | * 2. If we have multiple shared mappings of the same space in | ||
505 | * an object, we need to deal with the cache aliasing issues. | ||
506 | * | ||
507 | * Note that the pte lock will be held. | ||
508 | */ | ||
509 | void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, | ||
510 | pte_t *ptep) | ||
511 | { | ||
512 | unsigned long pfn = pte_pfn(*ptep); | ||
513 | struct address_space *mapping; | ||
514 | struct page *page; | ||
515 | |||
516 | if (!pfn_valid(pfn)) | ||
517 | return; | ||
518 | |||
519 | /* | ||
520 | * The zero page is never written to, so never has any dirty | ||
521 | * cache lines, and therefore never needs to be flushed. | ||
522 | */ | ||
523 | page = pfn_to_page(pfn); | ||
524 | if (page == ZERO_PAGE(0)) | ||
525 | return; | ||
526 | |||
527 | mapping = page_mapping(page); | ||
528 | if (!test_and_set_bit(PG_dcache_clean, &page->flags)) | ||
529 | __flush_dcache_page(mapping, page); | ||
530 | if (mapping) | ||
531 | if (vma->vm_flags & VM_EXEC) | ||
532 | __flush_icache_all(); | ||
533 | } | ||
diff --git a/arch/unicore32/mm/pgd.c b/arch/unicore32/mm/pgd.c new file mode 100644 index 000000000000..632cef7cd378 --- /dev/null +++ b/arch/unicore32/mm/pgd.c | |||
@@ -0,0 +1,102 @@ | |||
1 | /* | ||
2 | * linux/arch/unicore32/mm/pgd.c | ||
3 | * | ||
4 | * Code specific to PKUnity SoC and UniCore ISA | ||
5 | * | ||
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/gfp.h> | ||
14 | #include <linux/highmem.h> | ||
15 | |||
16 | #include <asm/pgalloc.h> | ||
17 | #include <asm/page.h> | ||
18 | #include <asm/tlbflush.h> | ||
19 | |||
20 | #include "mm.h" | ||
21 | |||
22 | #define FIRST_KERNEL_PGD_NR (FIRST_USER_PGD_NR + USER_PTRS_PER_PGD) | ||
23 | |||
24 | /* | ||
25 | * need to get a 4k page for level 1 | ||
26 | */ | ||
27 | pgd_t *get_pgd_slow(struct mm_struct *mm) | ||
28 | { | ||
29 | pgd_t *new_pgd, *init_pgd; | ||
30 | pmd_t *new_pmd, *init_pmd; | ||
31 | pte_t *new_pte, *init_pte; | ||
32 | |||
33 | new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 0); | ||
34 | if (!new_pgd) | ||
35 | goto no_pgd; | ||
36 | |||
37 | memset(new_pgd, 0, FIRST_KERNEL_PGD_NR * sizeof(pgd_t)); | ||
38 | |||
39 | /* | ||
40 | * Copy over the kernel and IO PGD entries | ||
41 | */ | ||
42 | init_pgd = pgd_offset_k(0); | ||
43 | memcpy(new_pgd + FIRST_KERNEL_PGD_NR, init_pgd + FIRST_KERNEL_PGD_NR, | ||
44 | (PTRS_PER_PGD - FIRST_KERNEL_PGD_NR) * sizeof(pgd_t)); | ||
45 | |||
46 | clean_dcache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t)); | ||
47 | |||
48 | if (!vectors_high()) { | ||
49 | /* | ||
50 | * On UniCore, first page must always be allocated since it | ||
51 | * contains the machine vectors. | ||
52 | */ | ||
53 | new_pmd = pmd_alloc(mm, (pud_t *)new_pgd, 0); | ||
54 | if (!new_pmd) | ||
55 | goto no_pmd; | ||
56 | |||
57 | new_pte = pte_alloc_map(mm, new_pmd, 0); | ||
58 | if (!new_pte) | ||
59 | goto no_pte; | ||
60 | |||
61 | init_pmd = pmd_offset((pud_t *)init_pgd, 0); | ||
62 | init_pte = pte_offset_map(init_pmd, 0); | ||
63 | set_pte(new_pte, *init_pte); | ||
64 | pte_unmap(init_pte); | ||
65 | pte_unmap(new_pte); | ||
66 | } | ||
67 | |||
68 | return new_pgd; | ||
69 | |||
70 | no_pte: | ||
71 | pmd_free(mm, new_pmd); | ||
72 | no_pmd: | ||
73 | free_pages((unsigned long)new_pgd, 0); | ||
74 | no_pgd: | ||
75 | return NULL; | ||
76 | } | ||
77 | |||
78 | void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd) | ||
79 | { | ||
80 | pmd_t *pmd; | ||
81 | pgtable_t pte; | ||
82 | |||
83 | if (!pgd) | ||
84 | return; | ||
85 | |||
86 | /* pgd is always present and good */ | ||
87 | pmd = pmd_off(pgd, 0); | ||
88 | if (pmd_none(*pmd)) | ||
89 | goto free; | ||
90 | if (pmd_bad(*pmd)) { | ||
91 | pmd_ERROR(*pmd); | ||
92 | pmd_clear(pmd); | ||
93 | goto free; | ||
94 | } | ||
95 | |||
96 | pte = pmd_pgtable(*pmd); | ||
97 | pmd_clear(pmd); | ||
98 | pte_free(mm, pte); | ||
99 | pmd_free(mm, pmd); | ||
100 | free: | ||
101 | free_pages((unsigned long) pgd, 0); | ||
102 | } | ||