diff options
Diffstat (limited to 'arch/arm/kernel/head.S')
-rw-r--r-- | arch/arm/kernel/head.S | 251 |
1 files changed, 196 insertions, 55 deletions
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index f17d9a09e8fb..c9173cfbbc74 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S | |||
@@ -26,14 +26,6 @@ | |||
26 | #include <mach/debug-macro.S> | 26 | #include <mach/debug-macro.S> |
27 | #endif | 27 | #endif |
28 | 28 | ||
29 | #if (PHYS_OFFSET & 0x001fffff) | ||
30 | #error "PHYS_OFFSET must be at an even 2MiB boundary!" | ||
31 | #endif | ||
32 | |||
33 | #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET) | ||
34 | #define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET) | ||
35 | |||
36 | |||
37 | /* | 29 | /* |
38 | * swapper_pg_dir is the virtual address of the initial page table. | 30 | * swapper_pg_dir is the virtual address of the initial page table. |
39 | * We place the page tables 16K below KERNEL_RAM_VADDR. Therefore, we must | 31 | * We place the page tables 16K below KERNEL_RAM_VADDR. Therefore, we must |
@@ -41,6 +33,7 @@ | |||
41 | * the least significant 16 bits to be 0x8000, but we could probably | 33 | * the least significant 16 bits to be 0x8000, but we could probably |
42 | * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000. | 34 | * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000. |
43 | */ | 35 | */ |
36 | #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET) | ||
44 | #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000 | 37 | #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000 |
45 | #error KERNEL_RAM_VADDR must start at 0xXXXX8000 | 38 | #error KERNEL_RAM_VADDR must start at 0xXXXX8000 |
46 | #endif | 39 | #endif |
@@ -48,8 +41,8 @@ | |||
48 | .globl swapper_pg_dir | 41 | .globl swapper_pg_dir |
49 | .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000 | 42 | .equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000 |
50 | 43 | ||
51 | .macro pgtbl, rd | 44 | .macro pgtbl, rd, phys |
52 | ldr \rd, =(KERNEL_RAM_PADDR - 0x4000) | 45 | add \rd, \phys, #TEXT_OFFSET - 0x4000 |
53 | .endm | 46 | .endm |
54 | 47 | ||
55 | #ifdef CONFIG_XIP_KERNEL | 48 | #ifdef CONFIG_XIP_KERNEL |
@@ -87,25 +80,33 @@ ENTRY(stext) | |||
87 | movs r10, r5 @ invalid processor (r5=0)? | 80 | movs r10, r5 @ invalid processor (r5=0)? |
88 | THUMB( it eq ) @ force fixup-able long branch encoding | 81 | THUMB( it eq ) @ force fixup-able long branch encoding |
89 | beq __error_p @ yes, error 'p' | 82 | beq __error_p @ yes, error 'p' |
90 | bl __lookup_machine_type @ r5=machinfo | 83 | |
91 | movs r8, r5 @ invalid machine (r5=0)? | 84 | #ifndef CONFIG_XIP_KERNEL |
92 | THUMB( it eq ) @ force fixup-able long branch encoding | 85 | adr r3, 2f |
93 | beq __error_a @ yes, error 'a' | 86 | ldmia r3, {r4, r8} |
87 | sub r4, r3, r4 @ (PHYS_OFFSET - PAGE_OFFSET) | ||
88 | add r8, r8, r4 @ PHYS_OFFSET | ||
89 | #else | ||
90 | ldr r8, =PLAT_PHYS_OFFSET | ||
91 | #endif | ||
94 | 92 | ||
95 | /* | 93 | /* |
96 | * r1 = machine no, r2 = atags, | 94 | * r1 = machine no, r2 = atags, |
97 | * r8 = machinfo, r9 = cpuid, r10 = procinfo | 95 | * r8 = phys_offset, r9 = cpuid, r10 = procinfo |
98 | */ | 96 | */ |
99 | bl __vet_atags | 97 | bl __vet_atags |
100 | #ifdef CONFIG_SMP_ON_UP | 98 | #ifdef CONFIG_SMP_ON_UP |
101 | bl __fixup_smp | 99 | bl __fixup_smp |
102 | #endif | 100 | #endif |
101 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT | ||
102 | bl __fixup_pv_table | ||
103 | #endif | ||
103 | bl __create_page_tables | 104 | bl __create_page_tables |
104 | 105 | ||
105 | /* | 106 | /* |
106 | * The following calls CPU specific code in a position independent | 107 | * The following calls CPU specific code in a position independent |
107 | * manner. See arch/arm/mm/proc-*.S for details. r10 = base of | 108 | * manner. See arch/arm/mm/proc-*.S for details. r10 = base of |
108 | * xxx_proc_info structure selected by __lookup_machine_type | 109 | * xxx_proc_info structure selected by __lookup_processor_type |
109 | * above. On return, the CPU will be ready for the MMU to be | 110 | * above. On return, the CPU will be ready for the MMU to be |
110 | * turned on, and r0 will hold the CPU control register value. | 111 | * turned on, and r0 will hold the CPU control register value. |
111 | */ | 112 | */ |
@@ -118,22 +119,24 @@ ENTRY(stext) | |||
118 | 1: b __enable_mmu | 119 | 1: b __enable_mmu |
119 | ENDPROC(stext) | 120 | ENDPROC(stext) |
120 | .ltorg | 121 | .ltorg |
122 | #ifndef CONFIG_XIP_KERNEL | ||
123 | 2: .long . | ||
124 | .long PAGE_OFFSET | ||
125 | #endif | ||
121 | 126 | ||
122 | /* | 127 | /* |
123 | * Setup the initial page tables. We only setup the barest | 128 | * Setup the initial page tables. We only setup the barest |
124 | * amount which are required to get the kernel running, which | 129 | * amount which are required to get the kernel running, which |
125 | * generally means mapping in the kernel code. | 130 | * generally means mapping in the kernel code. |
126 | * | 131 | * |
127 | * r8 = machinfo | 132 | * r8 = phys_offset, r9 = cpuid, r10 = procinfo |
128 | * r9 = cpuid | ||
129 | * r10 = procinfo | ||
130 | * | 133 | * |
131 | * Returns: | 134 | * Returns: |
132 | * r0, r3, r5-r7 corrupted | 135 | * r0, r3, r5-r7 corrupted |
133 | * r4 = physical page table address | 136 | * r4 = physical page table address |
134 | */ | 137 | */ |
135 | __create_page_tables: | 138 | __create_page_tables: |
136 | pgtbl r4 @ page table address | 139 | pgtbl r4, r8 @ page table address |
137 | 140 | ||
138 | /* | 141 | /* |
139 | * Clear the 16K level 1 swapper page table | 142 | * Clear the 16K level 1 swapper page table |
@@ -189,10 +192,8 @@ __create_page_tables: | |||
189 | /* | 192 | /* |
190 | * Map some ram to cover our .data and .bss areas. | 193 | * Map some ram to cover our .data and .bss areas. |
191 | */ | 194 | */ |
192 | orr r3, r7, #(KERNEL_RAM_PADDR & 0xff000000) | 195 | add r3, r8, #TEXT_OFFSET |
193 | .if (KERNEL_RAM_PADDR & 0x00f00000) | 196 | orr r3, r3, r7 |
194 | orr r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000) | ||
195 | .endif | ||
196 | add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18 | 197 | add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18 |
197 | str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]! | 198 | str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]! |
198 | ldr r6, =(_end - 1) | 199 | ldr r6, =(_end - 1) |
@@ -205,14 +206,17 @@ __create_page_tables: | |||
205 | #endif | 206 | #endif |
206 | 207 | ||
207 | /* | 208 | /* |
208 | * Then map first 1MB of ram in case it contains our boot params. | 209 | * Then map boot params address in r2 or |
210 | * the first 1MB of ram if boot params address is not specified. | ||
209 | */ | 211 | */ |
210 | add r0, r4, #PAGE_OFFSET >> 18 | 212 | mov r0, r2, lsr #20 |
211 | orr r6, r7, #(PHYS_OFFSET & 0xff000000) | 213 | movs r0, r0, lsl #20 |
212 | .if (PHYS_OFFSET & 0x00f00000) | 214 | moveq r0, r8 |
213 | orr r6, r6, #(PHYS_OFFSET & 0x00f00000) | 215 | sub r3, r0, r8 |
214 | .endif | 216 | add r3, r3, #PAGE_OFFSET |
215 | str r6, [r0] | 217 | add r3, r4, r3, lsr #18 |
218 | orr r6, r7, r0 | ||
219 | str r6, [r3] | ||
216 | 220 | ||
217 | #ifdef CONFIG_DEBUG_LL | 221 | #ifdef CONFIG_DEBUG_LL |
218 | #ifndef CONFIG_DEBUG_ICEDCC | 222 | #ifndef CONFIG_DEBUG_ICEDCC |
@@ -391,25 +395,24 @@ ENDPROC(__turn_mmu_on) | |||
391 | 395 | ||
392 | 396 | ||
393 | #ifdef CONFIG_SMP_ON_UP | 397 | #ifdef CONFIG_SMP_ON_UP |
398 | __INIT | ||
394 | __fixup_smp: | 399 | __fixup_smp: |
395 | mov r4, #0x00070000 | 400 | and r3, r9, #0x000f0000 @ architecture version |
396 | orr r3, r4, #0xff000000 @ mask 0xff070000 | 401 | teq r3, #0x000f0000 @ CPU ID supported? |
397 | orr r4, r4, #0x41000000 @ val 0x41070000 | ||
398 | and r0, r9, r3 | ||
399 | teq r0, r4 @ ARM CPU and ARMv6/v7? | ||
400 | bne __fixup_smp_on_up @ no, assume UP | 402 | bne __fixup_smp_on_up @ no, assume UP |
401 | 403 | ||
402 | orr r3, r3, #0x0000ff00 | 404 | bic r3, r9, #0x00ff0000 |
403 | orr r3, r3, #0x000000f0 @ mask 0xff07fff0 | 405 | bic r3, r3, #0x0000000f @ mask 0xff00fff0 |
406 | mov r4, #0x41000000 | ||
404 | orr r4, r4, #0x0000b000 | 407 | orr r4, r4, #0x0000b000 |
405 | orr r4, r4, #0x00000020 @ val 0x4107b020 | 408 | orr r4, r4, #0x00000020 @ val 0x4100b020 |
406 | and r0, r9, r3 | 409 | teq r3, r4 @ ARM 11MPCore? |
407 | teq r0, r4 @ ARM 11MPCore? | ||
408 | moveq pc, lr @ yes, assume SMP | 410 | moveq pc, lr @ yes, assume SMP |
409 | 411 | ||
410 | mrc p15, 0, r0, c0, c0, 5 @ read MPIDR | 412 | mrc p15, 0, r0, c0, c0, 5 @ read MPIDR |
411 | tst r0, #1 << 31 | 413 | and r0, r0, #0xc0000000 @ multiprocessing extensions and |
412 | movne pc, lr @ bit 31 => SMP | 414 | teq r0, #0x80000000 @ not part of a uniprocessor system? |
415 | moveq pc, lr @ yes, assume SMP | ||
413 | 416 | ||
414 | __fixup_smp_on_up: | 417 | __fixup_smp_on_up: |
415 | adr r0, 1f | 418 | adr r0, 1f |
@@ -417,18 +420,7 @@ __fixup_smp_on_up: | |||
417 | sub r3, r0, r3 | 420 | sub r3, r0, r3 |
418 | add r4, r4, r3 | 421 | add r4, r4, r3 |
419 | add r5, r5, r3 | 422 | add r5, r5, r3 |
420 | 2: cmp r4, r5 | 423 | b __do_fixup_smp_on_up |
421 | movhs pc, lr | ||
422 | ldmia r4!, {r0, r6} | ||
423 | ARM( str r6, [r0, r3] ) | ||
424 | THUMB( add r0, r0, r3 ) | ||
425 | #ifdef __ARMEB__ | ||
426 | THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian. | ||
427 | #endif | ||
428 | THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords | ||
429 | THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3. | ||
430 | THUMB( strh r6, [r0] ) | ||
431 | b 2b | ||
432 | ENDPROC(__fixup_smp) | 424 | ENDPROC(__fixup_smp) |
433 | 425 | ||
434 | .align | 426 | .align |
@@ -442,7 +434,156 @@ smp_on_up: | |||
442 | ALT_SMP(.long 1) | 434 | ALT_SMP(.long 1) |
443 | ALT_UP(.long 0) | 435 | ALT_UP(.long 0) |
444 | .popsection | 436 | .popsection |
437 | #endif | ||
438 | |||
439 | .text | ||
440 | __do_fixup_smp_on_up: | ||
441 | cmp r4, r5 | ||
442 | movhs pc, lr | ||
443 | ldmia r4!, {r0, r6} | ||
444 | ARM( str r6, [r0, r3] ) | ||
445 | THUMB( add r0, r0, r3 ) | ||
446 | #ifdef __ARMEB__ | ||
447 | THUMB( mov r6, r6, ror #16 ) @ Convert word order for big-endian. | ||
448 | #endif | ||
449 | THUMB( strh r6, [r0], #2 ) @ For Thumb-2, store as two halfwords | ||
450 | THUMB( mov r6, r6, lsr #16 ) @ to be robust against misaligned r3. | ||
451 | THUMB( strh r6, [r0] ) | ||
452 | b __do_fixup_smp_on_up | ||
453 | ENDPROC(__do_fixup_smp_on_up) | ||
454 | |||
455 | ENTRY(fixup_smp) | ||
456 | stmfd sp!, {r4 - r6, lr} | ||
457 | mov r4, r0 | ||
458 | add r5, r0, r1 | ||
459 | mov r3, #0 | ||
460 | bl __do_fixup_smp_on_up | ||
461 | ldmfd sp!, {r4 - r6, pc} | ||
462 | ENDPROC(fixup_smp) | ||
463 | |||
464 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT | ||
445 | 465 | ||
466 | /* __fixup_pv_table - patch the stub instructions with the delta between | ||
467 | * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and | ||
468 | * can be expressed by an immediate shifter operand. The stub instruction | ||
469 | * has a form of '(add|sub) rd, rn, #imm'. | ||
470 | */ | ||
471 | __HEAD | ||
472 | __fixup_pv_table: | ||
473 | adr r0, 1f | ||
474 | ldmia r0, {r3-r5, r7} | ||
475 | sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET | ||
476 | add r4, r4, r3 @ adjust table start address | ||
477 | add r5, r5, r3 @ adjust table end address | ||
478 | add r7, r7, r3 @ adjust __pv_phys_offset address | ||
479 | str r8, [r7] @ save computed PHYS_OFFSET to __pv_phys_offset | ||
480 | #ifndef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT | ||
481 | mov r6, r3, lsr #24 @ constant for add/sub instructions | ||
482 | teq r3, r6, lsl #24 @ must be 16MiB aligned | ||
483 | #else | ||
484 | mov r6, r3, lsr #16 @ constant for add/sub instructions | ||
485 | teq r3, r6, lsl #16 @ must be 64kiB aligned | ||
486 | #endif | ||
487 | THUMB( it ne @ cross section branch ) | ||
488 | bne __error | ||
489 | str r6, [r7, #4] @ save to __pv_offset | ||
490 | b __fixup_a_pv_table | ||
491 | ENDPROC(__fixup_pv_table) | ||
492 | |||
493 | .align | ||
494 | 1: .long . | ||
495 | .long __pv_table_begin | ||
496 | .long __pv_table_end | ||
497 | 2: .long __pv_phys_offset | ||
498 | |||
499 | .text | ||
500 | __fixup_a_pv_table: | ||
501 | #ifdef CONFIG_THUMB2_KERNEL | ||
502 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT | ||
503 | lsls r0, r6, #24 | ||
504 | lsr r6, #8 | ||
505 | beq 1f | ||
506 | clz r7, r0 | ||
507 | lsr r0, #24 | ||
508 | lsl r0, r7 | ||
509 | bic r0, 0x0080 | ||
510 | lsrs r7, #1 | ||
511 | orrcs r0, #0x0080 | ||
512 | orr r0, r0, r7, lsl #12 | ||
513 | #endif | ||
514 | 1: lsls r6, #24 | ||
515 | beq 4f | ||
516 | clz r7, r6 | ||
517 | lsr r6, #24 | ||
518 | lsl r6, r7 | ||
519 | bic r6, #0x0080 | ||
520 | lsrs r7, #1 | ||
521 | orrcs r6, #0x0080 | ||
522 | orr r6, r6, r7, lsl #12 | ||
523 | orr r6, #0x4000 | ||
524 | b 4f | ||
525 | 2: @ at this point the C flag is always clear | ||
526 | add r7, r3 | ||
527 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT | ||
528 | ldrh ip, [r7] | ||
529 | tst ip, 0x0400 @ the i bit tells us LS or MS byte | ||
530 | beq 3f | ||
531 | cmp r0, #0 @ set C flag, and ... | ||
532 | biceq ip, 0x0400 @ immediate zero value has a special encoding | ||
533 | streqh ip, [r7] @ that requires the i bit cleared | ||
534 | #endif | ||
535 | 3: ldrh ip, [r7, #2] | ||
536 | and ip, 0x8f00 | ||
537 | orrcc ip, r6 @ mask in offset bits 31-24 | ||
538 | orrcs ip, r0 @ mask in offset bits 23-16 | ||
539 | strh ip, [r7, #2] | ||
540 | 4: cmp r4, r5 | ||
541 | ldrcc r7, [r4], #4 @ use branch for delay slot | ||
542 | bcc 2b | ||
543 | bx lr | ||
544 | #else | ||
545 | #ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT | ||
546 | and r0, r6, #255 @ offset bits 23-16 | ||
547 | mov r6, r6, lsr #8 @ offset bits 31-24 | ||
548 | #else | ||
549 | mov r0, #0 @ just in case... | ||
550 | #endif | ||
551 | b 3f | ||
552 | 2: ldr ip, [r7, r3] | ||
553 | bic ip, ip, #0x000000ff | ||
554 | tst ip, #0x400 @ rotate shift tells us LS or MS byte | ||
555 | orrne ip, ip, r6 @ mask in offset bits 31-24 | ||
556 | orreq ip, ip, r0 @ mask in offset bits 23-16 | ||
557 | str ip, [r7, r3] | ||
558 | 3: cmp r4, r5 | ||
559 | ldrcc r7, [r4], #4 @ use branch for delay slot | ||
560 | bcc 2b | ||
561 | mov pc, lr | ||
562 | #endif | ||
563 | ENDPROC(__fixup_a_pv_table) | ||
564 | |||
565 | ENTRY(fixup_pv_table) | ||
566 | stmfd sp!, {r4 - r7, lr} | ||
567 | ldr r2, 2f @ get address of __pv_phys_offset | ||
568 | mov r3, #0 @ no offset | ||
569 | mov r4, r0 @ r0 = table start | ||
570 | add r5, r0, r1 @ r1 = table size | ||
571 | ldr r6, [r2, #4] @ get __pv_offset | ||
572 | bl __fixup_a_pv_table | ||
573 | ldmfd sp!, {r4 - r7, pc} | ||
574 | ENDPROC(fixup_pv_table) | ||
575 | |||
576 | .align | ||
577 | 2: .long __pv_phys_offset | ||
578 | |||
579 | .data | ||
580 | .globl __pv_phys_offset | ||
581 | .type __pv_phys_offset, %object | ||
582 | __pv_phys_offset: | ||
583 | .long 0 | ||
584 | .size __pv_phys_offset, . - __pv_phys_offset | ||
585 | __pv_offset: | ||
586 | .long 0 | ||
446 | #endif | 587 | #endif |
447 | 588 | ||
448 | #include "head-common.S" | 589 | #include "head-common.S" |