diff options
-rw-r--r-- | arch/arm/Kconfig | 13 | ||||
-rw-r--r-- | arch/arm/include/asm/memory.h | 55 | ||||
-rw-r--r-- | arch/arm/include/asm/module.h | 15 | ||||
-rw-r--r-- | arch/arm/kernel/armksyms.c | 4 | ||||
-rw-r--r-- | arch/arm/kernel/head.S | 68 | ||||
-rw-r--r-- | arch/arm/kernel/module.c | 23 | ||||
-rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 4 |
7 files changed, 167 insertions, 15 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5cff165b7eb0..4147f76e7988 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -191,6 +191,19 @@ config VECTORS_BASE | |||
191 | help | 191 | help |
192 | The base address of exception vectors. | 192 | The base address of exception vectors. |
193 | 193 | ||
194 | config ARM_PATCH_PHYS_VIRT | ||
195 | bool "Patch physical to virtual translations at runtime (EXPERIMENTAL)" | ||
196 | depends on EXPERIMENTAL | ||
197 | depends on !XIP_KERNEL && !THUMB2_KERNEL && MMU | ||
198 | depends on !ARCH_MSM | ||
199 | depends on !ARCH_REALVIEW || !SPARSEMEM | ||
200 | help | ||
201 | Patch phys-to-virt translation functions at runtime according to | ||
202 | the position of the kernel in system memory. | ||
203 | |||
204 | This can only be used with non-XIP, non-Thumb2, MMU kernels where | ||
205 | the base of physical memory is at a 16MB boundary. | ||
206 | |||
194 | source "init/Kconfig" | 207 | source "init/Kconfig" |
195 | 208 | ||
196 | source "kernel/Kconfig.freezer" | 209 | source "kernel/Kconfig.freezer" |
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 2efec578a62e..7197879e1cb7 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h | |||
@@ -24,8 +24,6 @@ | |||
24 | */ | 24 | */ |
25 | #define UL(x) _AC(x, UL) | 25 | #define UL(x) _AC(x, UL) |
26 | 26 | ||
27 | #define PHYS_OFFSET PLAT_PHYS_OFFSET | ||
28 | |||
29 | #ifdef CONFIG_MMU | 27 | #ifdef CONFIG_MMU |
30 | 28 | ||
31 | /* | 29 | /* |
@@ -135,16 +133,6 @@ | |||
135 | #endif | 133 | #endif |
136 | 134 | ||
137 | /* | 135 | /* |
138 | * Physical vs virtual RAM address space conversion. These are | ||
139 | * private definitions which should NOT be used outside memory.h | ||
140 | * files. Use virt_to_phys/phys_to_virt/__pa/__va instead. | ||
141 | */ | ||
142 | #ifndef __virt_to_phys | ||
143 | #define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET) | ||
144 | #define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET) | ||
145 | #endif | ||
146 | |||
147 | /* | ||
148 | * Convert a physical address to a Page Frame Number and back | 136 | * Convert a physical address to a Page Frame Number and back |
149 | */ | 137 | */ |
150 | #define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT) | 138 | #define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT) |
@@ -159,6 +147,49 @@ | |||
159 | #ifndef __ASSEMBLY__ | 147 | #ifndef __ASSEMBLY__ |
160 | 148 | ||
161 | /* | 149 | /* |
150 | * Physical vs virtual RAM address space conversion. These are | ||
151 | * private definitions which should NOT be used outside memory.h | ||
152 | * files. Use virt_to_phys/phys_to_virt/__pa/__va instead. | ||
153 | */ | ||
154 | #ifndef __virt_to_phys | ||
155 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT | ||
156 | |||
157 | extern unsigned long __pv_phys_offset; | ||
158 | #define PHYS_OFFSET __pv_phys_offset | ||
159 | |||
160 | #define __pv_stub(from,to,instr) \ | ||
161 | __asm__("@ __pv_stub\n" \ | ||
162 | "1: " instr " %0, %1, %2\n" \ | ||
163 | " .pushsection .pv_table,\"a\"\n" \ | ||
164 | " .long 1b\n" \ | ||
165 | " .popsection\n" \ | ||
166 | : "=r" (to) \ | ||
167 | : "r" (from), "I" (0x81000000)) | ||
168 | |||
169 | static inline unsigned long __virt_to_phys(unsigned long x) | ||
170 | { | ||
171 | unsigned long t; | ||
172 | __pv_stub(x, t, "add"); | ||
173 | return t; | ||
174 | } | ||
175 | |||
176 | static inline unsigned long __phys_to_virt(unsigned long x) | ||
177 | { | ||
178 | unsigned long t; | ||
179 | __pv_stub(x, t, "sub"); | ||
180 | return t; | ||
181 | } | ||
182 | #else | ||
183 | #define __virt_to_phys(x) ((x) - PAGE_OFFSET + PHYS_OFFSET) | ||
184 | #define __phys_to_virt(x) ((x) - PHYS_OFFSET + PAGE_OFFSET) | ||
185 | #endif | ||
186 | #endif | ||
187 | |||
188 | #ifndef PHYS_OFFSET | ||
189 | #define PHYS_OFFSET PLAT_PHYS_OFFSET | ||
190 | #endif | ||
191 | |||
192 | /* | ||
162 | * The DMA mask corresponding to the maximum bus address allocatable | 193 | * The DMA mask corresponding to the maximum bus address allocatable |
163 | * using GFP_DMA. The default here places no restriction on DMA | 194 | * using GFP_DMA. The default here places no restriction on DMA |
164 | * allocations. This must be the smallest DMA mask in the system, | 195 | * allocations. This must be the smallest DMA mask in the system, |
diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h index 12c8e680cbff..d072c21332ee 100644 --- a/arch/arm/include/asm/module.h +++ b/arch/arm/include/asm/module.h | |||
@@ -25,8 +25,19 @@ struct mod_arch_specific { | |||
25 | }; | 25 | }; |
26 | 26 | ||
27 | /* | 27 | /* |
28 | * Include the ARM architecture version. | 28 | * Add the ARM architecture version to the version magic string |
29 | */ | 29 | */ |
30 | #define MODULE_ARCH_VERMAGIC "ARMv" __stringify(__LINUX_ARM_ARCH__) " " | 30 | #define MODULE_ARCH_VERMAGIC_ARMVSN "ARMv" __stringify(__LINUX_ARM_ARCH__) " " |
31 | |||
32 | /* Add __virt_to_phys patching state as well */ | ||
33 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT | ||
34 | #define MODULE_ARCH_VERMAGIC_P2V "p2v8 " | ||
35 | #else | ||
36 | #define MODULE_ARCH_VERMAGIC_P2V "" | ||
37 | #endif | ||
38 | |||
39 | #define MODULE_ARCH_VERMAGIC \ | ||
40 | MODULE_ARCH_VERMAGIC_ARMVSN \ | ||
41 | MODULE_ARCH_VERMAGIC_P2V | ||
31 | 42 | ||
32 | #endif /* _ASM_ARM_MODULE_H */ | 43 | #endif /* _ASM_ARM_MODULE_H */ |
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index e5e1e5387678..9615423c37dd 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c | |||
@@ -170,3 +170,7 @@ EXPORT_SYMBOL(mcount); | |||
170 | #endif | 170 | #endif |
171 | EXPORT_SYMBOL(__gnu_mcount_nc); | 171 | EXPORT_SYMBOL(__gnu_mcount_nc); |
172 | #endif | 172 | #endif |
173 | |||
174 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT | ||
175 | EXPORT_SYMBOL(__pv_phys_offset); | ||
176 | #endif | ||
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 03a588b6e15c..1db8ead2e331 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S | |||
@@ -98,6 +98,9 @@ ENTRY(stext) | |||
98 | #ifdef CONFIG_SMP_ON_UP | 98 | #ifdef CONFIG_SMP_ON_UP |
99 | bl __fixup_smp | 99 | bl __fixup_smp |
100 | #endif | 100 | #endif |
101 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT | ||
102 | bl __fixup_pv_table | ||
103 | #endif | ||
101 | bl __create_page_tables | 104 | bl __create_page_tables |
102 | 105 | ||
103 | /* | 106 | /* |
@@ -438,4 +441,69 @@ smp_on_up: | |||
438 | 441 | ||
439 | #endif | 442 | #endif |
440 | 443 | ||
444 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT | ||
445 | |||
446 | /* __fixup_pv_table - patch the stub instructions with the delta between | ||
447 | * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and | ||
448 | * can be expressed by an immediate shifter operand. The stub instruction | ||
449 | * has a form of '(add|sub) rd, rn, #imm'. | ||
450 | */ | ||
451 | __HEAD | ||
452 | __fixup_pv_table: | ||
453 | adr r0, 1f | ||
454 | ldmia r0, {r3-r5, r7} | ||
455 | sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET | ||
456 | add r4, r4, r3 @ adjust table start address | ||
457 | add r5, r5, r3 @ adjust table end address | ||
458 | str r8, [r7, r3]! @ save computed PHYS_OFFSET to __pv_phys_offset | ||
459 | mov r6, r3, lsr #24 @ constant for add/sub instructions | ||
460 | teq r3, r6, lsl #24 @ must be 16MiB aligned | ||
461 | bne __error | ||
462 | str r6, [r7, #4] @ save to __pv_offset | ||
463 | b __fixup_a_pv_table | ||
464 | ENDPROC(__fixup_pv_table) | ||
465 | |||
466 | .align | ||
467 | 1: .long . | ||
468 | .long __pv_table_begin | ||
469 | .long __pv_table_end | ||
470 | 2: .long __pv_phys_offset | ||
471 | |||
472 | .text | ||
473 | __fixup_a_pv_table: | ||
474 | b 3f | ||
475 | 2: ldr ip, [r7, r3] | ||
476 | bic ip, ip, #0x000000ff | ||
477 | orr ip, ip, r6 | ||
478 | str ip, [r7, r3] | ||
479 | 3: cmp r4, r5 | ||
480 | ldrcc r7, [r4], #4 @ use branch for delay slot | ||
481 | bcc 2b | ||
482 | mov pc, lr | ||
483 | ENDPROC(__fixup_a_pv_table) | ||
484 | |||
485 | ENTRY(fixup_pv_table) | ||
486 | stmfd sp!, {r4 - r7, lr} | ||
487 | ldr r2, 2f @ get address of __pv_phys_offset | ||
488 | mov r3, #0 @ no offset | ||
489 | mov r4, r0 @ r0 = table start | ||
490 | add r5, r0, r1 @ r1 = table size | ||
491 | ldr r6, [r2, #4] @ get __pv_offset | ||
492 | bl __fixup_a_pv_table | ||
493 | ldmfd sp!, {r4 - r7, pc} | ||
494 | ENDPROC(fixup_pv_table) | ||
495 | |||
496 | .align | ||
497 | 2: .long __pv_phys_offset | ||
498 | |||
499 | .data | ||
500 | .globl __pv_phys_offset | ||
501 | .type __pv_phys_offset, %object | ||
502 | __pv_phys_offset: | ||
503 | .long 0 | ||
504 | .size __pv_phys_offset, . - __pv_phys_offset | ||
505 | __pv_offset: | ||
506 | .long 0 | ||
507 | #endif | ||
508 | |||
441 | #include "head-common.S" | 509 | #include "head-common.S" |
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c index 2cfe8161b478..c5679f6d9f64 100644 --- a/arch/arm/kernel/module.c +++ b/arch/arm/kernel/module.c | |||
@@ -268,12 +268,28 @@ struct mod_unwind_map { | |||
268 | const Elf_Shdr *txt_sec; | 268 | const Elf_Shdr *txt_sec; |
269 | }; | 269 | }; |
270 | 270 | ||
271 | static const Elf_Shdr *find_mod_section(const Elf32_Ehdr *hdr, | ||
272 | const Elf_Shdr *sechdrs, const char *name) | ||
273 | { | ||
274 | const Elf_Shdr *s, *se; | ||
275 | const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; | ||
276 | |||
277 | for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) | ||
278 | if (strcmp(name, secstrs + s->sh_name) == 0) | ||
279 | return s; | ||
280 | |||
281 | return NULL; | ||
282 | } | ||
283 | |||
284 | extern void fixup_pv_table(const void *, unsigned long); | ||
285 | |||
271 | int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, | 286 | int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, |
272 | struct module *mod) | 287 | struct module *mod) |
273 | { | 288 | { |
289 | const Elf_Shdr *s = NULL; | ||
274 | #ifdef CONFIG_ARM_UNWIND | 290 | #ifdef CONFIG_ARM_UNWIND |
275 | const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; | 291 | const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; |
276 | const Elf_Shdr *s, *sechdrs_end = sechdrs + hdr->e_shnum; | 292 | const Elf_Shdr *sechdrs_end = sechdrs + hdr->e_shnum; |
277 | struct mod_unwind_map maps[ARM_SEC_MAX]; | 293 | struct mod_unwind_map maps[ARM_SEC_MAX]; |
278 | int i; | 294 | int i; |
279 | 295 | ||
@@ -315,6 +331,11 @@ int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, | |||
315 | maps[i].txt_sec->sh_addr, | 331 | maps[i].txt_sec->sh_addr, |
316 | maps[i].txt_sec->sh_size); | 332 | maps[i].txt_sec->sh_size); |
317 | #endif | 333 | #endif |
334 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT | ||
335 | s = find_mod_section(hdr, sechdrs, ".pv_table"); | ||
336 | if (s) | ||
337 | fixup_pv_table((void *)s->sh_addr, s->sh_size); | ||
338 | #endif | ||
318 | return 0; | 339 | return 0; |
319 | } | 340 | } |
320 | 341 | ||
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 86b66f3f2031..45b5651777ee 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S | |||
@@ -57,6 +57,10 @@ SECTIONS | |||
57 | __smpalt_end = .; | 57 | __smpalt_end = .; |
58 | #endif | 58 | #endif |
59 | 59 | ||
60 | __pv_table_begin = .; | ||
61 | *(.pv_table) | ||
62 | __pv_table_end = .; | ||
63 | |||
60 | INIT_SETUP(16) | 64 | INIT_SETUP(16) |
61 | 65 | ||
62 | INIT_CALLS | 66 | INIT_CALLS |