aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2011-01-04 14:09:43 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2011-02-17 18:27:32 -0500
commitdc21af99fadcfa0ae65b52fd0895f85824f0c288 (patch)
treed2d293d79fdb405f25ca7fb18aa16aba6ecea261
parent5b7de4547b388d3949620e21d072e35b6f2638fa (diff)
ARM: P2V: introduce phys_to_virt/virt_to_phys runtime patching
This idea came from Nicolas, Eric Miao produced an initial version, which was then rewritten into this. Patch the physical to virtual translations at runtime. As we modify the code, this makes it incompatible with XIP kernels, but allows us to achieve this with minimal loss of performance. As many translations are of the form: physical = virtual + (PHYS_OFFSET - PAGE_OFFSET) virtual = physical - (PHYS_OFFSET - PAGE_OFFSET) we generate an 'add' instruction for __virt_to_phys(), and a 'sub' instruction for __phys_to_virt(). We calculate at run time (PHYS_OFFSET - PAGE_OFFSET) by comparing the address prior to MMU initialization with where it should be once the MMU has been initialized, and place this constant into the above add/sub instructions. Once we have (PHYS_OFFSET - PAGE_OFFSET), we can calculate the real PHYS_OFFSET as PAGE_OFFSET is a build-time constant, and save this for the C-mode PHYS_OFFSET variable definition to use. At present, we are unable to support Realview with Sparsemem enabled as this uses a complex mapping function, and MSM as this requires a constant which will not fit in our math instruction. Add a module version magic string for this feature to prevent incompatible modules being loaded. Tested-by: Tony Lindgren <tony@atomide.com> Reviewed-by: Nicolas Pitre <nicolas.pitre@linaro.org> Tested-by: Nicolas Pitre <nicolas.pitre@linaro.org> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/Kconfig13
-rw-r--r--arch/arm/include/asm/memory.h55
-rw-r--r--arch/arm/include/asm/module.h15
-rw-r--r--arch/arm/kernel/armksyms.c4
-rw-r--r--arch/arm/kernel/head.S68
-rw-r--r--arch/arm/kernel/module.c23
-rw-r--r--arch/arm/kernel/vmlinux.lds.S4
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
194config 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
194source "init/Kconfig" 207source "init/Kconfig"
195 208
196source "kernel/Kconfig.freezer" 209source "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
157extern 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
169static 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
176static 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
171EXPORT_SYMBOL(__gnu_mcount_nc); 171EXPORT_SYMBOL(__gnu_mcount_nc);
172#endif 172#endif
173
174#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
175EXPORT_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
464ENDPROC(__fixup_pv_table)
465
466 .align
4671: .long .
468 .long __pv_table_begin
469 .long __pv_table_end
4702: .long __pv_phys_offset
471
472 .text
473__fixup_a_pv_table:
474 b 3f
4752: ldr ip, [r7, r3]
476 bic ip, ip, #0x000000ff
477 orr ip, ip, r6
478 str ip, [r7, r3]
4793: cmp r4, r5
480 ldrcc r7, [r4], #4 @ use branch for delay slot
481 bcc 2b
482 mov pc, lr
483ENDPROC(__fixup_a_pv_table)
484
485ENTRY(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}
494ENDPROC(fixup_pv_table)
495
496 .align
4972: .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
271static 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
284extern void fixup_pv_table(const void *, unsigned long);
285
271int module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs, 286int 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