Skip to content

Commit f52bb72

Browse files
SricharantiSantosh Shilimkar
authored andcommitted
ARM: mm: Correct virt_to_phys patching for 64 bit physical addresses
The current phys_to_virt patching mechanism works only for 32 bit physical addresses and this patch extends the idea for 64bit physical addresses. The 64bit v2p patching mechanism patches the higher 8 bits of physical address with a constant using 'mov' instruction and lower 32bits are patched using 'add'. While this is correct, in those platforms where the lowmem addressable physical memory spawns across 4GB boundary, a carry bit can be produced as a result of addition of lower 32bits. This has to be taken in to account and added in to the upper. The patched __pv_offset and va are added in lower 32bits, where __pv_offset can be in two's complement form when PA_START < VA_START and that can result in a false carry bit. e.g 1) PA = 0x80000000; VA = 0xC0000000 __pv_offset = PA - VA = 0xC0000000 (2's complement) 2) PA = 0x2 80000000; VA = 0xC000000 __pv_offset = PA - VA = 0x1 C0000000 So adding __pv_offset + VA should never result in a true overflow for (1). So in order to differentiate between a true carry, a __pv_offset is extended to 64bit and the upper 32bits will have 0xffffffff if __pv_offset is 2's complement. So 'mvn #0' is inserted instead of 'mov' while patching for the same reason. Since mov, add, sub instruction are to patched with different constants inside the same stub, the rotation field of the opcode is using to differentiate between them. So the above examples for v2p translation becomes for VA=0xC0000000, 1) PA[63:32] = 0xffffffff PA[31:0] = VA + 0xC0000000 --> results in a carry PA[63:32] = PA[63:32] + carry PA[63:0] = 0x0 80000000 2) PA[63:32] = 0x1 PA[31:0] = VA + 0xC0000000 --> results in a carry PA[63:32] = PA[63:32] + carry PA[63:0] = 0x2 80000000 The above ideas were suggested by Nicolas Pitre <nico@linaro.org> as part of the review of first and second versions of the subject patch. There is no corresponding change on the phys_to_virt() side, because computations on the upper 32-bits would be discarded anyway. Cc: Russell King <linux@arm.linux.org.uk> Reviewed-by: Nicolas Pitre <nico@linaro.org> Signed-off-by: Sricharan R <r.sricharan@ti.com> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
1 parent c1a5f4f commit f52bb72

File tree

3 files changed

+82
-19
lines changed

3 files changed

+82
-19
lines changed

arch/arm/include/asm/memory.h

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,14 @@
172172
* so that all we need to do is modify the 8-bit constant field.
173173
*/
174174
#define __PV_BITS_31_24 0x81000000
175+
#define __PV_BITS_7_0 0x81
175176

176177
extern phys_addr_t (*arch_virt_to_idmap) (unsigned long x);
177-
extern unsigned long __pv_phys_offset;
178+
extern u64 __pv_phys_offset;
179+
extern u64 __pv_offset;
180+
extern void fixup_pv_table(const void *, unsigned long);
181+
extern const void *__pv_table_begin, *__pv_table_end;
182+
178183
#define PHYS_OFFSET __pv_phys_offset
179184

180185
#define __pv_stub(from,to,instr,type) \
@@ -186,10 +191,36 @@ extern unsigned long __pv_phys_offset;
186191
: "=r" (to) \
187192
: "r" (from), "I" (type))
188193

194+
#define __pv_stub_mov_hi(t) \
195+
__asm__ volatile("@ __pv_stub_mov\n" \
196+
"1: mov %R0, %1\n" \
197+
" .pushsection .pv_table,\"a\"\n" \
198+
" .long 1b\n" \
199+
" .popsection\n" \
200+
: "=r" (t) \
201+
: "I" (__PV_BITS_7_0))
202+
203+
#define __pv_add_carry_stub(x, y) \
204+
__asm__ volatile("@ __pv_add_carry_stub\n" \
205+
"1: adds %Q0, %1, %2\n" \
206+
" adc %R0, %R0, #0\n" \
207+
" .pushsection .pv_table,\"a\"\n" \
208+
" .long 1b\n" \
209+
" .popsection\n" \
210+
: "+r" (y) \
211+
: "r" (x), "I" (__PV_BITS_31_24) \
212+
: "cc")
213+
189214
static inline phys_addr_t __virt_to_phys(unsigned long x)
190215
{
191-
unsigned long t;
192-
__pv_stub(x, t, "add", __PV_BITS_31_24);
216+
phys_addr_t t;
217+
218+
if (sizeof(phys_addr_t) == 4) {
219+
__pv_stub(x, t, "add", __PV_BITS_31_24);
220+
} else {
221+
__pv_stub_mov_hi(t);
222+
__pv_add_carry_stub(x, t);
223+
}
193224
return t;
194225
}
195226

arch/arm/kernel/armksyms.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,5 @@ EXPORT_SYMBOL(__gnu_mcount_nc);
155155

156156
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
157157
EXPORT_SYMBOL(__pv_phys_offset);
158+
EXPORT_SYMBOL(__pv_offset);
158159
#endif

arch/arm/kernel/head.S

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,14 @@ ENTRY(fixup_smp)
536536
ldmfd sp!, {r4 - r6, pc}
537537
ENDPROC(fixup_smp)
538538

539+
#ifdef __ARMEB_
540+
#define LOW_OFFSET 0x4
541+
#define HIGH_OFFSET 0x0
542+
#else
543+
#define LOW_OFFSET 0x0
544+
#define HIGH_OFFSET 0x4
545+
#endif
546+
539547
#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
540548

541549
/* __fixup_pv_table - patch the stub instructions with the delta between
@@ -546,17 +554,20 @@ ENDPROC(fixup_smp)
546554
__HEAD
547555
__fixup_pv_table:
548556
adr r0, 1f
549-
ldmia r0, {r3-r5, r7}
550-
sub r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
557+
ldmia r0, {r3-r7}
558+
mvn ip, #0
559+
subs r3, r0, r3 @ PHYS_OFFSET - PAGE_OFFSET
551560
add r4, r4, r3 @ adjust table start address
552561
add r5, r5, r3 @ adjust table end address
553-
add r7, r7, r3 @ adjust __pv_phys_offset address
554-
str r8, [r7] @ save computed PHYS_OFFSET to __pv_phys_offset
562+
add r6, r6, r3 @ adjust __pv_phys_offset address
563+
add r7, r7, r3 @ adjust __pv_offset address
564+
str r8, [r6, #LOW_OFFSET] @ save computed PHYS_OFFSET to __pv_phys_offset
565+
strcc ip, [r7, #HIGH_OFFSET] @ save to __pv_offset high bits
555566
mov r6, r3, lsr #24 @ constant for add/sub instructions
556567
teq r3, r6, lsl #24 @ must be 16MiB aligned
557568
THUMB( it ne @ cross section branch )
558569
bne __error
559-
str r6, [r7, #4] @ save to __pv_offset
570+
str r3, [r7, #LOW_OFFSET] @ save to __pv_offset low bits
560571
b __fixup_a_pv_table
561572
ENDPROC(__fixup_pv_table)
562573

@@ -565,10 +576,19 @@ ENDPROC(__fixup_pv_table)
565576
.long __pv_table_begin
566577
.long __pv_table_end
567578
2: .long __pv_phys_offset
579+
.long __pv_offset
568580

569581
.text
570582
__fixup_a_pv_table:
583+
adr r0, 3f
584+
ldr r6, [r0]
585+
add r6, r6, r3
586+
ldr r0, [r6, #HIGH_OFFSET] @ pv_offset high word
587+
ldr r6, [r6, #LOW_OFFSET] @ pv_offset low word
588+
mov r6, r6, lsr #24
589+
cmn r0, #1
571590
#ifdef CONFIG_THUMB2_KERNEL
591+
moveq r0, #0x200000 @ set bit 21, mov to mvn instruction
572592
lsls r6, #24
573593
beq 2f
574594
clz r7, r6
@@ -582,18 +602,28 @@ __fixup_a_pv_table:
582602
b 2f
583603
1: add r7, r3
584604
ldrh ip, [r7, #2]
585-
and ip, 0x8f00
586-
orr ip, r6 @ mask in offset bits 31-24
605+
tst ip, #0x4000
606+
and ip, #0x8f00
607+
orrne ip, r6 @ mask in offset bits 31-24
608+
orreq ip, r0 @ mask in offset bits 7-0
587609
strh ip, [r7, #2]
610+
ldrheq ip, [r7]
611+
biceq ip, #0x20
612+
orreq ip, ip, r0, lsr #16
613+
strheq ip, [r7]
588614
2: cmp r4, r5
589615
ldrcc r7, [r4], #4 @ use branch for delay slot
590616
bcc 1b
591617
bx lr
592618
#else
619+
moveq r0, #0x400000 @ set bit 22, mov to mvn instruction
593620
b 2f
594621
1: ldr ip, [r7, r3]
595622
bic ip, ip, #0x000000ff
596-
orr ip, ip, r6 @ mask in offset bits 31-24
623+
tst ip, #0xf00 @ check the rotation field
624+
orrne ip, ip, r6 @ mask in offset bits 31-24
625+
biceq ip, ip, #0x400000 @ clear bit 22
626+
orreq ip, ip, r0 @ mask in offset bits 7-0
597627
str ip, [r7, r3]
598628
2: cmp r4, r5
599629
ldrcc r7, [r4], #4 @ use branch for delay slot
@@ -602,28 +632,29 @@ __fixup_a_pv_table:
602632
#endif
603633
ENDPROC(__fixup_a_pv_table)
604634

635+
3: .long __pv_offset
636+
605637
ENTRY(fixup_pv_table)
606638
stmfd sp!, {r4 - r7, lr}
607-
ldr r2, 2f @ get address of __pv_phys_offset
608639
mov r3, #0 @ no offset
609640
mov r4, r0 @ r0 = table start
610641
add r5, r0, r1 @ r1 = table size
611-
ldr r6, [r2, #4] @ get __pv_offset
612642
bl __fixup_a_pv_table
613643
ldmfd sp!, {r4 - r7, pc}
614644
ENDPROC(fixup_pv_table)
615645

616-
.align
617-
2: .long __pv_phys_offset
618-
619646
.data
620647
.globl __pv_phys_offset
621648
.type __pv_phys_offset, %object
622649
__pv_phys_offset:
623-
.long 0
624-
.size __pv_phys_offset, . - __pv_phys_offset
650+
.quad 0
651+
.size __pv_phys_offset, . -__pv_phys_offset
652+
653+
.globl __pv_offset
654+
.type __pv_offset, %object
625655
__pv_offset:
626-
.long 0
656+
.quad 0
657+
.size __pv_offset, . -__pv_offset
627658
#endif
628659

629660
#include "head-common.S"

0 commit comments

Comments
 (0)