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 |
