aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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