diff options
| -rw-r--r-- | arch/arm/include/asm/highmem.h | 15 | ||||
| -rw-r--r-- | arch/arm/include/asm/kmap_types.h | 1 | ||||
| -rw-r--r-- | arch/arm/include/asm/ucontext.h | 23 | ||||
| -rw-r--r-- | arch/arm/include/asm/user.h | 12 | ||||
| -rw-r--r-- | arch/arm/kernel/signal.c | 93 | ||||
| -rw-r--r-- | arch/arm/mach-at91/Makefile | 4 | ||||
| -rw-r--r-- | arch/arm/mach-at91/pm_slowclock.S | 4 | ||||
| -rw-r--r-- | arch/arm/mm/copypage-v6.c | 9 | ||||
| -rw-r--r-- | arch/arm/mm/dma-mapping.c | 5 | ||||
| -rw-r--r-- | arch/arm/mm/flush.c | 25 | ||||
| -rw-r--r-- | arch/arm/mm/highmem.c | 87 | ||||
| -rw-r--r-- | arch/arm/mm/mmu.c | 10 | ||||
| -rw-r--r-- | arch/arm/vfp/vfpmodule.c | 31 |
13 files changed, 251 insertions, 68 deletions
diff --git a/arch/arm/include/asm/highmem.h b/arch/arm/include/asm/highmem.h index 7f36d00600b4..feb988a7ec37 100644 --- a/arch/arm/include/asm/highmem.h +++ b/arch/arm/include/asm/highmem.h | |||
| @@ -11,7 +11,11 @@ | |||
| 11 | 11 | ||
| 12 | #define kmap_prot PAGE_KERNEL | 12 | #define kmap_prot PAGE_KERNEL |
| 13 | 13 | ||
| 14 | #define flush_cache_kmaps() flush_cache_all() | 14 | #define flush_cache_kmaps() \ |
| 15 | do { \ | ||
| 16 | if (cache_is_vivt()) \ | ||
| 17 | flush_cache_all(); \ | ||
| 18 | } while (0) | ||
| 15 | 19 | ||
| 16 | extern pte_t *pkmap_page_table; | 20 | extern pte_t *pkmap_page_table; |
| 17 | 21 | ||
| @@ -21,11 +25,20 @@ extern void *kmap_high(struct page *page); | |||
| 21 | extern void *kmap_high_get(struct page *page); | 25 | extern void *kmap_high_get(struct page *page); |
| 22 | extern void kunmap_high(struct page *page); | 26 | extern void kunmap_high(struct page *page); |
| 23 | 27 | ||
| 28 | extern void *kmap_high_l1_vipt(struct page *page, pte_t *saved_pte); | ||
| 29 | extern void kunmap_high_l1_vipt(struct page *page, pte_t saved_pte); | ||
| 30 | |||
| 31 | /* | ||
| 32 | * The following functions are already defined by <linux/highmem.h> | ||
| 33 | * when CONFIG_HIGHMEM is not set. | ||
| 34 | */ | ||
| 35 | #ifdef CONFIG_HIGHMEM | ||
| 24 | extern void *kmap(struct page *page); | 36 | extern void *kmap(struct page *page); |
| 25 | extern void kunmap(struct page *page); | 37 | extern void kunmap(struct page *page); |
| 26 | extern void *kmap_atomic(struct page *page, enum km_type type); | 38 | extern void *kmap_atomic(struct page *page, enum km_type type); |
| 27 | extern void kunmap_atomic(void *kvaddr, enum km_type type); | 39 | extern void kunmap_atomic(void *kvaddr, enum km_type type); |
| 28 | extern void *kmap_atomic_pfn(unsigned long pfn, enum km_type type); | 40 | extern void *kmap_atomic_pfn(unsigned long pfn, enum km_type type); |
| 29 | extern struct page *kmap_atomic_to_page(const void *ptr); | 41 | extern struct page *kmap_atomic_to_page(const void *ptr); |
| 42 | #endif | ||
| 30 | 43 | ||
| 31 | #endif | 44 | #endif |
diff --git a/arch/arm/include/asm/kmap_types.h b/arch/arm/include/asm/kmap_types.h index c019949a5189..c4b2ea3fbe42 100644 --- a/arch/arm/include/asm/kmap_types.h +++ b/arch/arm/include/asm/kmap_types.h | |||
| @@ -18,6 +18,7 @@ enum km_type { | |||
| 18 | KM_IRQ1, | 18 | KM_IRQ1, |
| 19 | KM_SOFTIRQ0, | 19 | KM_SOFTIRQ0, |
| 20 | KM_SOFTIRQ1, | 20 | KM_SOFTIRQ1, |
| 21 | KM_L1_CACHE, | ||
| 21 | KM_L2_CACHE, | 22 | KM_L2_CACHE, |
| 22 | KM_TYPE_NR | 23 | KM_TYPE_NR |
| 23 | }; | 24 | }; |
diff --git a/arch/arm/include/asm/ucontext.h b/arch/arm/include/asm/ucontext.h index bf65e9f4525d..47f023aa8495 100644 --- a/arch/arm/include/asm/ucontext.h +++ b/arch/arm/include/asm/ucontext.h | |||
| @@ -59,23 +59,22 @@ struct iwmmxt_sigframe { | |||
| 59 | #endif /* CONFIG_IWMMXT */ | 59 | #endif /* CONFIG_IWMMXT */ |
| 60 | 60 | ||
| 61 | #ifdef CONFIG_VFP | 61 | #ifdef CONFIG_VFP |
| 62 | #if __LINUX_ARM_ARCH__ < 6 | ||
| 63 | /* For ARM pre-v6, we use fstmiax and fldmiax. This adds one extra | ||
| 64 | * word after the registers, and a word of padding at the end for | ||
| 65 | * alignment. */ | ||
| 66 | #define VFP_MAGIC 0x56465001 | 62 | #define VFP_MAGIC 0x56465001 |
| 67 | #define VFP_STORAGE_SIZE 152 | ||
| 68 | #else | ||
| 69 | #define VFP_MAGIC 0x56465002 | ||
| 70 | #define VFP_STORAGE_SIZE 144 | ||
| 71 | #endif | ||
| 72 | 63 | ||
| 73 | struct vfp_sigframe | 64 | struct vfp_sigframe |
| 74 | { | 65 | { |
| 75 | unsigned long magic; | 66 | unsigned long magic; |
| 76 | unsigned long size; | 67 | unsigned long size; |
| 77 | union vfp_state storage; | 68 | struct user_vfp ufp; |
| 78 | }; | 69 | struct user_vfp_exc ufp_exc; |
| 70 | } __attribute__((__aligned__(8))); | ||
| 71 | |||
| 72 | /* | ||
| 73 | * 8 byte for magic and size, 264 byte for ufp, 12 bytes for ufp_exc, | ||
| 74 | * 4 bytes padding. | ||
| 75 | */ | ||
| 76 | #define VFP_STORAGE_SIZE sizeof(struct vfp_sigframe) | ||
| 77 | |||
| 79 | #endif /* CONFIG_VFP */ | 78 | #endif /* CONFIG_VFP */ |
| 80 | 79 | ||
| 81 | /* | 80 | /* |
| @@ -91,7 +90,7 @@ struct aux_sigframe { | |||
| 91 | #ifdef CONFIG_IWMMXT | 90 | #ifdef CONFIG_IWMMXT |
| 92 | struct iwmmxt_sigframe iwmmxt; | 91 | struct iwmmxt_sigframe iwmmxt; |
| 93 | #endif | 92 | #endif |
| 94 | #if 0 && defined CONFIG_VFP /* Not yet saved. */ | 93 | #ifdef CONFIG_VFP |
| 95 | struct vfp_sigframe vfp; | 94 | struct vfp_sigframe vfp; |
| 96 | #endif | 95 | #endif |
| 97 | /* Something that isn't a valid magic number for any coprocessor. */ | 96 | /* Something that isn't a valid magic number for any coprocessor. */ |
diff --git a/arch/arm/include/asm/user.h b/arch/arm/include/asm/user.h index df95e050f9dd..05ac4b06876a 100644 --- a/arch/arm/include/asm/user.h +++ b/arch/arm/include/asm/user.h | |||
| @@ -83,11 +83,21 @@ struct user{ | |||
| 83 | 83 | ||
| 84 | /* | 84 | /* |
| 85 | * User specific VFP registers. If only VFPv2 is present, registers 16 to 31 | 85 | * User specific VFP registers. If only VFPv2 is present, registers 16 to 31 |
| 86 | * are ignored by the ptrace system call. | 86 | * are ignored by the ptrace system call and the signal handler. |
| 87 | */ | 87 | */ |
| 88 | struct user_vfp { | 88 | struct user_vfp { |
| 89 | unsigned long long fpregs[32]; | 89 | unsigned long long fpregs[32]; |
| 90 | unsigned long fpscr; | 90 | unsigned long fpscr; |
| 91 | }; | 91 | }; |
| 92 | 92 | ||
| 93 | /* | ||
| 94 | * VFP exception registers exposed to user space during signal delivery. | ||
| 95 | * Fields not relavant to the current VFP architecture are ignored. | ||
| 96 | */ | ||
| 97 | struct user_vfp_exc { | ||
| 98 | unsigned long fpexc; | ||
| 99 | unsigned long fpinst; | ||
| 100 | unsigned long fpinst2; | ||
| 101 | }; | ||
| 102 | |||
| 93 | #endif /* _ARM_USER_H */ | 103 | #endif /* _ARM_USER_H */ |
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index e7714f367eb8..907d5a620bca 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <asm/cacheflush.h> | 18 | #include <asm/cacheflush.h> |
| 19 | #include <asm/ucontext.h> | 19 | #include <asm/ucontext.h> |
| 20 | #include <asm/unistd.h> | 20 | #include <asm/unistd.h> |
| 21 | #include <asm/vfp.h> | ||
| 21 | 22 | ||
| 22 | #include "ptrace.h" | 23 | #include "ptrace.h" |
| 23 | #include "signal.h" | 24 | #include "signal.h" |
| @@ -175,6 +176,90 @@ static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame) | |||
| 175 | 176 | ||
| 176 | #endif | 177 | #endif |
| 177 | 178 | ||
| 179 | #ifdef CONFIG_VFP | ||
| 180 | |||
| 181 | static int preserve_vfp_context(struct vfp_sigframe __user *frame) | ||
| 182 | { | ||
| 183 | struct thread_info *thread = current_thread_info(); | ||
| 184 | struct vfp_hard_struct *h = &thread->vfpstate.hard; | ||
| 185 | const unsigned long magic = VFP_MAGIC; | ||
| 186 | const unsigned long size = VFP_STORAGE_SIZE; | ||
| 187 | int err = 0; | ||
| 188 | |||
| 189 | vfp_sync_hwstate(thread); | ||
| 190 | __put_user_error(magic, &frame->magic, err); | ||
| 191 | __put_user_error(size, &frame->size, err); | ||
| 192 | |||
| 193 | /* | ||
| 194 | * Copy the floating point registers. There can be unused | ||
| 195 | * registers see asm/hwcap.h for details. | ||
| 196 | */ | ||
| 197 | err |= __copy_to_user(&frame->ufp.fpregs, &h->fpregs, | ||
| 198 | sizeof(h->fpregs)); | ||
| 199 | /* | ||
| 200 | * Copy the status and control register. | ||
| 201 | */ | ||
| 202 | __put_user_error(h->fpscr, &frame->ufp.fpscr, err); | ||
| 203 | |||
| 204 | /* | ||
| 205 | * Copy the exception registers. | ||
| 206 | */ | ||
| 207 | __put_user_error(h->fpexc, &frame->ufp_exc.fpexc, err); | ||
| 208 | __put_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); | ||
| 209 | __put_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); | ||
| 210 | |||
| 211 | return err ? -EFAULT : 0; | ||
| 212 | } | ||
| 213 | |||
| 214 | static int restore_vfp_context(struct vfp_sigframe __user *frame) | ||
| 215 | { | ||
| 216 | struct thread_info *thread = current_thread_info(); | ||
| 217 | struct vfp_hard_struct *h = &thread->vfpstate.hard; | ||
| 218 | unsigned long magic; | ||
| 219 | unsigned long size; | ||
| 220 | unsigned long fpexc; | ||
| 221 | int err = 0; | ||
| 222 | |||
| 223 | __get_user_error(magic, &frame->magic, err); | ||
| 224 | __get_user_error(size, &frame->size, err); | ||
| 225 | |||
| 226 | if (err) | ||
| 227 | return -EFAULT; | ||
| 228 | if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE) | ||
| 229 | return -EINVAL; | ||
| 230 | |||
| 231 | /* | ||
| 232 | * Copy the floating point registers. There can be unused | ||
| 233 | * registers see asm/hwcap.h for details. | ||
| 234 | */ | ||
| 235 | err |= __copy_from_user(&h->fpregs, &frame->ufp.fpregs, | ||
| 236 | sizeof(h->fpregs)); | ||
| 237 | /* | ||
| 238 | * Copy the status and control register. | ||
| 239 | */ | ||
| 240 | __get_user_error(h->fpscr, &frame->ufp.fpscr, err); | ||
| 241 | |||
| 242 | /* | ||
| 243 | * Sanitise and restore the exception registers. | ||
| 244 | */ | ||
| 245 | __get_user_error(fpexc, &frame->ufp_exc.fpexc, err); | ||
| 246 | /* Ensure the VFP is enabled. */ | ||
| 247 | fpexc |= FPEXC_EN; | ||
| 248 | /* Ensure FPINST2 is invalid and the exception flag is cleared. */ | ||
| 249 | fpexc &= ~(FPEXC_EX | FPEXC_FP2V); | ||
| 250 | h->fpexc = fpexc; | ||
| 251 | |||
| 252 | __get_user_error(h->fpinst, &frame->ufp_exc.fpinst, err); | ||
| 253 | __get_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err); | ||
| 254 | |||
| 255 | if (!err) | ||
| 256 | vfp_flush_hwstate(thread); | ||
| 257 | |||
| 258 | return err ? -EFAULT : 0; | ||
| 259 | } | ||
| 260 | |||
| 261 | #endif | ||
| 262 | |||
| 178 | /* | 263 | /* |
| 179 | * Do a signal return; undo the signal stack. These are aligned to 64-bit. | 264 | * Do a signal return; undo the signal stack. These are aligned to 64-bit. |
| 180 | */ | 265 | */ |
| @@ -233,8 +318,8 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf) | |||
| 233 | err |= restore_iwmmxt_context(&aux->iwmmxt); | 318 | err |= restore_iwmmxt_context(&aux->iwmmxt); |
| 234 | #endif | 319 | #endif |
| 235 | #ifdef CONFIG_VFP | 320 | #ifdef CONFIG_VFP |
| 236 | // if (err == 0) | 321 | if (err == 0) |
| 237 | // err |= vfp_restore_state(&sf->aux.vfp); | 322 | err |= restore_vfp_context(&aux->vfp); |
| 238 | #endif | 323 | #endif |
| 239 | 324 | ||
| 240 | return err; | 325 | return err; |
| @@ -348,8 +433,8 @@ setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs, sigset_t *set) | |||
| 348 | err |= preserve_iwmmxt_context(&aux->iwmmxt); | 433 | err |= preserve_iwmmxt_context(&aux->iwmmxt); |
| 349 | #endif | 434 | #endif |
| 350 | #ifdef CONFIG_VFP | 435 | #ifdef CONFIG_VFP |
| 351 | // if (err == 0) | 436 | if (err == 0) |
| 352 | // err |= vfp_save_state(&sf->aux.vfp); | 437 | err |= preserve_vfp_context(&aux->vfp); |
| 353 | #endif | 438 | #endif |
| 354 | __put_user_error(0, &aux->end_magic, err); | 439 | __put_user_error(0, &aux->end_magic, err); |
| 355 | 440 | ||
diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index 027dd570dcc3..d4004557532a 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile | |||
| @@ -16,8 +16,8 @@ obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_d | |||
| 16 | obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o | 16 | obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o |
| 17 | obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o sam9_smc.o | 17 | obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o sam9_smc.o |
| 18 | obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o | 18 | obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o |
| 19 | obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o | 19 | obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o |
| 20 | obj-$(CONFIG_ARCH_AT91SAM9G45) += at91sam9g45.o at91sam926x_time.o at91sam9g45_devices.o sam9_smc.o | 20 | obj-$(CONFIG_ARCH_AT91SAM9G45) += at91sam9g45.o at91sam926x_time.o at91sam9g45_devices.o sam9_smc.o |
| 21 | obj-$(CONFIG_ARCH_AT91CAP9) += at91cap9.o at91sam926x_time.o at91cap9_devices.o sam9_smc.o | 21 | obj-$(CONFIG_ARCH_AT91CAP9) += at91cap9.o at91sam926x_time.o at91cap9_devices.o sam9_smc.o |
| 22 | obj-$(CONFIG_ARCH_AT572D940HF) += at572d940hf.o at91sam926x_time.o at572d940hf_devices.o sam9_smc.o | 22 | obj-$(CONFIG_ARCH_AT572D940HF) += at572d940hf.o at91sam926x_time.o at572d940hf_devices.o sam9_smc.o |
| 23 | obj-$(CONFIG_ARCH_AT91X40) += at91x40.o at91x40_time.o | 23 | obj-$(CONFIG_ARCH_AT91X40) += at91x40.o at91x40_time.o |
diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S index 9fcbd6ca0090..9c5b48e68a71 100644 --- a/arch/arm/mach-at91/pm_slowclock.S +++ b/arch/arm/mach-at91/pm_slowclock.S | |||
| @@ -175,8 +175,6 @@ ENTRY(at91_slow_clock) | |||
| 175 | orr r3, r3, #(1 << 29) /* bit 29 always set */ | 175 | orr r3, r3, #(1 << 29) /* bit 29 always set */ |
| 176 | str r3, [r1, #(AT91_CKGR_PLLAR - AT91_PMC)] | 176 | str r3, [r1, #(AT91_CKGR_PLLAR - AT91_PMC)] |
| 177 | 177 | ||
| 178 | wait_pllalock | ||
| 179 | |||
| 180 | /* Save PLLB setting and disable it */ | 178 | /* Save PLLB setting and disable it */ |
| 181 | ldr r3, [r1, #(AT91_CKGR_PLLBR - AT91_PMC)] | 179 | ldr r3, [r1, #(AT91_CKGR_PLLBR - AT91_PMC)] |
| 182 | str r3, .saved_pllbr | 180 | str r3, .saved_pllbr |
| @@ -184,8 +182,6 @@ ENTRY(at91_slow_clock) | |||
| 184 | mov r3, #AT91_PMC_PLLCOUNT | 182 | mov r3, #AT91_PMC_PLLCOUNT |
| 185 | str r3, [r1, #(AT91_CKGR_PLLBR - AT91_PMC)] | 183 | str r3, [r1, #(AT91_CKGR_PLLBR - AT91_PMC)] |
| 186 | 184 | ||
| 187 | wait_pllblock | ||
| 188 | |||
| 189 | /* Turn off the main oscillator */ | 185 | /* Turn off the main oscillator */ |
| 190 | ldr r3, [r1, #(AT91_CKGR_MOR - AT91_PMC)] | 186 | ldr r3, [r1, #(AT91_CKGR_MOR - AT91_PMC)] |
| 191 | bic r3, r3, #AT91_PMC_MOSCEN | 187 | bic r3, r3, #AT91_PMC_MOSCEN |
diff --git a/arch/arm/mm/copypage-v6.c b/arch/arm/mm/copypage-v6.c index 8bca4dea6dfa..f55fa1044f72 100644 --- a/arch/arm/mm/copypage-v6.c +++ b/arch/arm/mm/copypage-v6.c | |||
| @@ -41,14 +41,7 @@ static void v6_copy_user_highpage_nonaliasing(struct page *to, | |||
| 41 | kfrom = kmap_atomic(from, KM_USER0); | 41 | kfrom = kmap_atomic(from, KM_USER0); |
| 42 | kto = kmap_atomic(to, KM_USER1); | 42 | kto = kmap_atomic(to, KM_USER1); |
| 43 | copy_page(kto, kfrom); | 43 | copy_page(kto, kfrom); |
| 44 | #ifdef CONFIG_HIGHMEM | 44 | __cpuc_flush_dcache_area(kto, PAGE_SIZE); |
| 45 | /* | ||
| 46 | * kmap_atomic() doesn't set the page virtual address, and | ||
| 47 | * kunmap_atomic() takes care of cache flushing already. | ||
| 48 | */ | ||
| 49 | if (page_address(to) != NULL) | ||
| 50 | #endif | ||
| 51 | __cpuc_flush_dcache_area(kto, PAGE_SIZE); | ||
| 52 | kunmap_atomic(kto, KM_USER1); | 45 | kunmap_atomic(kto, KM_USER1); |
| 53 | kunmap_atomic(kfrom, KM_USER0); | 46 | kunmap_atomic(kfrom, KM_USER0); |
| 54 | } | 47 | } |
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 1351edc0b26f..13fa536d82e6 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c | |||
| @@ -464,6 +464,11 @@ static void dma_cache_maint_page(struct page *page, unsigned long offset, | |||
| 464 | vaddr += offset; | 464 | vaddr += offset; |
| 465 | op(vaddr, len, dir); | 465 | op(vaddr, len, dir); |
| 466 | kunmap_high(page); | 466 | kunmap_high(page); |
| 467 | } else if (cache_is_vipt()) { | ||
| 468 | pte_t saved_pte; | ||
| 469 | vaddr = kmap_high_l1_vipt(page, &saved_pte); | ||
| 470 | op(vaddr + offset, len, dir); | ||
| 471 | kunmap_high_l1_vipt(page, saved_pte); | ||
| 467 | } | 472 | } |
| 468 | } else { | 473 | } else { |
| 469 | vaddr = page_address(page) + offset; | 474 | vaddr = page_address(page) + offset; |
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c index e34f095e2090..c6844cb9b508 100644 --- a/arch/arm/mm/flush.c +++ b/arch/arm/mm/flush.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | #include <asm/cacheflush.h> | 14 | #include <asm/cacheflush.h> |
| 15 | #include <asm/cachetype.h> | 15 | #include <asm/cachetype.h> |
| 16 | #include <asm/highmem.h> | ||
| 16 | #include <asm/smp_plat.h> | 17 | #include <asm/smp_plat.h> |
| 17 | #include <asm/system.h> | 18 | #include <asm/system.h> |
| 18 | #include <asm/tlbflush.h> | 19 | #include <asm/tlbflush.h> |
| @@ -152,21 +153,25 @@ void copy_to_user_page(struct vm_area_struct *vma, struct page *page, | |||
| 152 | 153 | ||
| 153 | void __flush_dcache_page(struct address_space *mapping, struct page *page) | 154 | void __flush_dcache_page(struct address_space *mapping, struct page *page) |
| 154 | { | 155 | { |
| 155 | void *addr = page_address(page); | ||
| 156 | |||
| 157 | /* | 156 | /* |
| 158 | * Writeback any data associated with the kernel mapping of this | 157 | * Writeback any data associated with the kernel mapping of this |
| 159 | * page. This ensures that data in the physical page is mutually | 158 | * page. This ensures that data in the physical page is mutually |
| 160 | * coherent with the kernels mapping. | 159 | * coherent with the kernels mapping. |
| 161 | */ | 160 | */ |
| 162 | #ifdef CONFIG_HIGHMEM | 161 | if (!PageHighMem(page)) { |
| 163 | /* | 162 | __cpuc_flush_dcache_area(page_address(page), PAGE_SIZE); |
| 164 | * kmap_atomic() doesn't set the page virtual address, and | 163 | } else { |
| 165 | * kunmap_atomic() takes care of cache flushing already. | 164 | void *addr = kmap_high_get(page); |
| 166 | */ | 165 | if (addr) { |
| 167 | if (addr) | 166 | __cpuc_flush_dcache_area(addr, PAGE_SIZE); |
| 168 | #endif | 167 | kunmap_high(page); |
| 169 | __cpuc_flush_dcache_area(addr, PAGE_SIZE); | 168 | } else if (cache_is_vipt()) { |
| 169 | pte_t saved_pte; | ||
| 170 | addr = kmap_high_l1_vipt(page, &saved_pte); | ||
| 171 | __cpuc_flush_dcache_area(addr, PAGE_SIZE); | ||
| 172 | kunmap_high_l1_vipt(page, saved_pte); | ||
| 173 | } | ||
| 174 | } | ||
| 170 | 175 | ||
| 171 | /* | 176 | /* |
| 172 | * If this is a page cache page, and we have an aliasing VIPT cache, | 177 | * If this is a page cache page, and we have an aliasing VIPT cache, |
diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c index 2be1ec7c1b41..77b030f5ec09 100644 --- a/arch/arm/mm/highmem.c +++ b/arch/arm/mm/highmem.c | |||
| @@ -79,7 +79,8 @@ void kunmap_atomic(void *kvaddr, enum km_type type) | |||
| 79 | unsigned int idx = type + KM_TYPE_NR * smp_processor_id(); | 79 | unsigned int idx = type + KM_TYPE_NR * smp_processor_id(); |
| 80 | 80 | ||
| 81 | if (kvaddr >= (void *)FIXADDR_START) { | 81 | if (kvaddr >= (void *)FIXADDR_START) { |
| 82 | __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); | 82 | if (cache_is_vivt()) |
| 83 | __cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE); | ||
| 83 | #ifdef CONFIG_DEBUG_HIGHMEM | 84 | #ifdef CONFIG_DEBUG_HIGHMEM |
| 84 | BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx)); | 85 | BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx)); |
| 85 | set_pte_ext(TOP_PTE(vaddr), __pte(0), 0); | 86 | set_pte_ext(TOP_PTE(vaddr), __pte(0), 0); |
| @@ -124,3 +125,87 @@ struct page *kmap_atomic_to_page(const void *ptr) | |||
| 124 | pte = TOP_PTE(vaddr); | 125 | pte = TOP_PTE(vaddr); |
| 125 | return pte_page(*pte); | 126 | return pte_page(*pte); |
| 126 | } | 127 | } |
| 128 | |||
| 129 | #ifdef CONFIG_CPU_CACHE_VIPT | ||
| 130 | |||
| 131 | #include <linux/percpu.h> | ||
| 132 | |||
| 133 | /* | ||
| 134 | * The VIVT cache of a highmem page is always flushed before the page | ||
| 135 | * is unmapped. Hence unmapped highmem pages need no cache maintenance | ||
| 136 | * in that case. | ||
| 137 | * | ||
| 138 | * However unmapped pages may still be cached with a VIPT cache, and | ||
| 139 | * it is not possible to perform cache maintenance on them using physical | ||
| 140 | * addresses unfortunately. So we have no choice but to set up a temporary | ||
| 141 | * virtual mapping for that purpose. | ||
| 142 | * | ||
| 143 | * Yet this VIPT cache maintenance may be triggered from DMA support | ||
| 144 | * functions which are possibly called from interrupt context. As we don't | ||
| 145 | * want to keep interrupt disabled all the time when such maintenance is | ||
| 146 | * taking place, we therefore allow for some reentrancy by preserving and | ||
| 147 | * restoring the previous fixmap entry before the interrupted context is | ||
| 148 | * resumed. If the reentrancy depth is 0 then there is no need to restore | ||
| 149 | * the previous fixmap, and leaving the current one in place allow it to | ||
| 150 | * be reused the next time without a TLB flush (common with DMA). | ||
| 151 | */ | ||
| 152 | |||
| 153 | static DEFINE_PER_CPU(int, kmap_high_l1_vipt_depth); | ||
| 154 | |||
| 155 | void *kmap_high_l1_vipt(struct page *page, pte_t *saved_pte) | ||
| 156 | { | ||
| 157 | unsigned int idx, cpu = smp_processor_id(); | ||
| 158 | int *depth = &per_cpu(kmap_high_l1_vipt_depth, cpu); | ||
| 159 | unsigned long vaddr, flags; | ||
| 160 | pte_t pte, *ptep; | ||
| 161 | |||
| 162 | idx = KM_L1_CACHE + KM_TYPE_NR * cpu; | ||
| 163 | vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); | ||
| 164 | ptep = TOP_PTE(vaddr); | ||
| 165 | pte = mk_pte(page, kmap_prot); | ||
| 166 | |||
| 167 | if (!in_interrupt()) | ||
| 168 | preempt_disable(); | ||
| 169 | |||
| 170 | raw_local_irq_save(flags); | ||
| 171 | (*depth)++; | ||
| 172 | if (pte_val(*ptep) == pte_val(pte)) { | ||
| 173 | *saved_pte = pte; | ||
| 174 | } else { | ||
| 175 | *saved_pte = *ptep; | ||
| 176 | set_pte_ext(ptep, pte, 0); | ||
| 177 | local_flush_tlb_kernel_page(vaddr); | ||
| 178 | } | ||
| 179 | raw_local_irq_restore(flags); | ||
| 180 | |||
| 181 | return (void *)vaddr; | ||
| 182 | } | ||
| 183 | |||
| 184 | void kunmap_high_l1_vipt(struct page *page, pte_t saved_pte) | ||
| 185 | { | ||
| 186 | unsigned int idx, cpu = smp_processor_id(); | ||
| 187 | int *depth = &per_cpu(kmap_high_l1_vipt_depth, cpu); | ||
| 188 | unsigned long vaddr, flags; | ||
| 189 | pte_t pte, *ptep; | ||
| 190 | |||
| 191 | idx = KM_L1_CACHE + KM_TYPE_NR * cpu; | ||
| 192 | vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx); | ||
| 193 | ptep = TOP_PTE(vaddr); | ||
| 194 | pte = mk_pte(page, kmap_prot); | ||
| 195 | |||
| 196 | BUG_ON(pte_val(*ptep) != pte_val(pte)); | ||
| 197 | BUG_ON(*depth <= 0); | ||
| 198 | |||
| 199 | raw_local_irq_save(flags); | ||
| 200 | (*depth)--; | ||
| 201 | if (*depth != 0 && pte_val(pte) != pte_val(saved_pte)) { | ||
| 202 | set_pte_ext(ptep, saved_pte, 0); | ||
| 203 | local_flush_tlb_kernel_page(vaddr); | ||
| 204 | } | ||
| 205 | raw_local_irq_restore(flags); | ||
| 206 | |||
| 207 | if (!in_interrupt()) | ||
| 208 | preempt_enable(); | ||
| 209 | } | ||
| 210 | |||
| 211 | #endif /* CONFIG_CPU_CACHE_VIPT */ | ||
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 4223d086aa17..241c24a1c18f 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c | |||
| @@ -1054,10 +1054,12 @@ void setup_mm_for_reboot(char mode) | |||
| 1054 | pgd_t *pgd; | 1054 | pgd_t *pgd; |
| 1055 | int i; | 1055 | int i; |
| 1056 | 1056 | ||
| 1057 | if (current->mm && current->mm->pgd) | 1057 | /* |
| 1058 | pgd = current->mm->pgd; | 1058 | * We need to access to user-mode page tables here. For kernel threads |
| 1059 | else | 1059 | * we don't have any user-mode mappings so we use the context that we |
| 1060 | pgd = init_mm.pgd; | 1060 | * "borrowed". |
| 1061 | */ | ||
| 1062 | pgd = current->active_mm->pgd; | ||
| 1061 | 1063 | ||
| 1062 | base_pmdval = PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_TYPE_SECT; | 1064 | base_pmdval = PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_TYPE_SECT; |
| 1063 | if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale()) | 1065 | if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale()) |
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index a420cb949328..315a540c7ce5 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c | |||
| @@ -428,26 +428,6 @@ static void vfp_pm_init(void) | |||
| 428 | static inline void vfp_pm_init(void) { } | 428 | static inline void vfp_pm_init(void) { } |
| 429 | #endif /* CONFIG_PM */ | 429 | #endif /* CONFIG_PM */ |
| 430 | 430 | ||
| 431 | /* | ||
| 432 | * Synchronise the hardware VFP state of a thread other than current with the | ||
| 433 | * saved one. This function is used by the ptrace mechanism. | ||
| 434 | */ | ||
| 435 | #ifdef CONFIG_SMP | ||
| 436 | void vfp_sync_hwstate(struct thread_info *thread) | ||
| 437 | { | ||
| 438 | } | ||
| 439 | |||
| 440 | void vfp_flush_hwstate(struct thread_info *thread) | ||
| 441 | { | ||
| 442 | /* | ||
| 443 | * On SMP systems, the VFP state is automatically saved at every | ||
| 444 | * context switch. We mark the thread VFP state as belonging to a | ||
| 445 | * non-existent CPU so that the saved one will be reloaded when | ||
| 446 | * needed. | ||
| 447 | */ | ||
| 448 | thread->vfpstate.hard.cpu = NR_CPUS; | ||
| 449 | } | ||
| 450 | #else | ||
| 451 | void vfp_sync_hwstate(struct thread_info *thread) | 431 | void vfp_sync_hwstate(struct thread_info *thread) |
| 452 | { | 432 | { |
| 453 | unsigned int cpu = get_cpu(); | 433 | unsigned int cpu = get_cpu(); |
| @@ -490,9 +470,18 @@ void vfp_flush_hwstate(struct thread_info *thread) | |||
| 490 | last_VFP_context[cpu] = NULL; | 470 | last_VFP_context[cpu] = NULL; |
| 491 | } | 471 | } |
| 492 | 472 | ||
| 473 | #ifdef CONFIG_SMP | ||
| 474 | /* | ||
| 475 | * For SMP we still have to take care of the case where the thread | ||
| 476 | * migrates to another CPU and then back to the original CPU on which | ||
| 477 | * the last VFP user is still the same thread. Mark the thread VFP | ||
| 478 | * state as belonging to a non-existent CPU so that the saved one will | ||
| 479 | * be reloaded in the above case. | ||
| 480 | */ | ||
| 481 | thread->vfpstate.hard.cpu = NR_CPUS; | ||
| 482 | #endif | ||
| 493 | put_cpu(); | 483 | put_cpu(); |
| 494 | } | 484 | } |
| 495 | #endif | ||
| 496 | 485 | ||
| 497 | #include <linux/smp.h> | 486 | #include <linux/smp.h> |
| 498 | 487 | ||
