| 1 | /* SPDX-License-Identifier: GPL-2.0-only */
|
| 2 | /*
|
| 3 | * Copyright (C) 1994-2002 Russell King
|
| 4 | * Copyright (c) 2003, 2020 ARM Limited
|
| 5 | * All Rights Reserved
|
| 6 | */
|
| 7 |
|
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 |
|
| 13 | |
| 14 | |
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | |
| 20 |
|
| 21 | /*
|
| 22 | * __fixup_pv_table - patch the stub instructions with the delta between
|
| 23 | * PHYS_OFFSET and PAGE_OFFSET, which is assumed to be
|
| 24 | * 2 MiB aligned.
|
| 25 | *
|
| 26 | * Called from head.S, which expects the following registers to be preserved:
|
| 27 | * r1 = machine no, r2 = atags or dtb,
|
| 28 | * r8 = phys_offset, r9 = cpuid, r10 = procinfo
|
| 29 | */
|
| 30 | __HEAD
|
| 31 | ENTRY(__fixup_pv_table)
|
| 32 | mov r0, r8, lsr |
| 33 | str_l r0, __pv_phys_pfn_offset, r3
|
| 34 |
|
| 35 | adr_l r0, __pv_offset
|
| 36 | subs r3, r8, |
| 37 | mvn ip, |
| 38 | strcc ip, [r0, |
| 39 | str r3, [r0, |
| 40 |
|
| 41 | mov r0, r3, lsr |
| 42 | teq r3, r0, lsl |
| 43 | bne 0f
|
| 44 |
|
| 45 | adr_l r4, __pv_table_begin
|
| 46 | adr_l r5, __pv_table_end
|
| 47 | b __fixup_a_pv_table
|
| 48 |
|
| 49 | 0: mov r0, r0 @ deadloop on error
|
| 50 | b 0b
|
| 51 | ENDPROC(__fixup_pv_table)
|
| 52 |
|
| 53 | .text
|
| 54 | __fixup_a_pv_table:
|
| 55 | adr_l r6, __pv_offset
|
| 56 | ldr r0, [r6, |
| 57 | ldr r6, [r6, |
| 58 | cmn r0, |
| 59 | |
| 60 | @
|
| 61 | @ The Thumb-2 versions of the patchable sequences are
|
| 62 | @
|
| 63 | @ phys-to-virt: movw <reg>, |
| 64 | @ lsl <reg>, |
| 65 | @ sub <VA>, <PA>, <reg>
|
| 66 | @
|
| 67 | @ virt-to-phys (non-LPAE): movw <reg>, |
| 68 | @ lsl <reg>, |
| 69 | @ add <PA>, <VA>, <reg>
|
| 70 | @
|
| 71 | @ virt-to-phys (LPAE): movw <reg>, |
| 72 | @ lsl <reg>, |
| 73 | @ adds <PAlo>, <VA>, <reg>
|
| 74 | @ mov <PAhi>, |
| 75 | @ adc <PAhi>, <PAhi>, |
| 76 | @
|
| 77 | @ In the non-LPAE case, all patchable instructions are MOVW
|
| 78 | @ instructions, where we need to patch in the offset into the
|
| 79 | @ second halfword of the opcode (the 16-bit immediate is encoded
|
| 80 | @ as imm4:i:imm3:imm8)
|
| 81 | @
|
| 82 | @ 15 11 10 9 4 3 0 15 14 12 11 8 7 0
|
| 83 | @ +-----------+---+-------------+------++---+------+----+------+
|
| 84 | @ MOVW | 1 1 1 1 0 | i | 1 0 0 1 0 0 | imm4 || 0 | imm3 | Rd | imm8 |
|
| 85 | @ +-----------+---+-------------+------++---+------+----+------+
|
| 86 | @
|
| 87 | @ In the LPAE case, we also need to patch in the high word of the
|
| 88 | @ offset into the immediate field of the MOV instruction, or patch it
|
| 89 | @ to a MVN instruction if the offset is negative. In this case, we
|
| 90 | @ need to inspect the first halfword of the opcode, to check whether
|
| 91 | @ it is MOVW or MOV/MVN, and to perform the MOV to MVN patching if
|
| 92 | @ needed. The encoding of the immediate is rather complex for values
|
| 93 | @ of i:imm3 != 0b0000, but fortunately, we never need more than 8 lower
|
| 94 | @ order bits, which can be patched into imm8 directly (and i:imm3
|
| 95 | @ cleared)
|
| 96 | @
|
| 97 | @ 15 11 10 9 5 0 15 14 12 11 8 7 0
|
| 98 | @ +-----------+---+---------------------++---+------+----+------+
|
| 99 | @ MOV | 1 1 1 1 0 | i | 0 0 0 1 0 0 1 1 1 1 || 0 | imm3 | Rd | imm8 |
|
| 100 | @ MVN | 1 1 1 1 0 | i | 0 0 0 1 1 0 1 1 1 1 || 0 | imm3 | Rd | imm8 |
|
| 101 | @ +-----------+---+---------------------++---+------+----+------+
|
| 102 | @
|
| 103 | moveq r0, |
| 104 | lsrs r3, r6, |
| 105 | ubfx r6, r6, |
| 106 | bfi r6, r3, |
| 107 | b .Lnext
|
| 108 | .Lloop: add r7, r4
|
| 109 | adds r4, |
| 110 | |
| 111 | ldrh ip, [r7]
|
| 112 | ARM_BE8(rev16 ip, ip)
|
| 113 | tst ip, |
| 114 | bne 0f @ skip to MOVW handling (Z flag is clear)
|
| 115 | bic ip, |
| 116 | orr ip, ip, r0, lsr |
| 117 | ARM_BE8(rev16 ip, ip)
|
| 118 | strh ip, [r7]
|
| 119 | @ Z flag is set
|
| 120 | 0:
|
| 121 | |
| 122 | ldrh ip, [r7, |
| 123 | ARM_BE8(rev16 ip, ip)
|
| 124 | and ip, |
| 125 | orreq ip, r0 @ Z flag set -> MOV/MVN -> patch in high bits
|
| 126 | orrne ip, r6 @ Z flag clear -> MOVW -> patch in low bits
|
| 127 | ARM_BE8(rev16 ip, ip)
|
| 128 | strh ip, [r7, |
| 129 | |
| 130 | |
| 131 | @ in BE8, we load data in BE, but instructions still in LE
|
| 132 | |
| 133 | |
| 134 | |
| 135 | |
| 136 | |
| 137 | |
| 138 | |
| 139 | |
| 140 |
|
| 141 | @
|
| 142 | @ The ARM versions of the patchable sequences are
|
| 143 | @
|
| 144 | @ phys-to-virt: sub <VA>, <PA>, |
| 145 | @ sub <VA>, <PA>, |
| 146 | @
|
| 147 | @ virt-to-phys (non-LPAE): add <PA>, <VA>, |
| 148 | @ add <PA>, <VA>, |
| 149 | @
|
| 150 | @ virt-to-phys (LPAE): movw <reg>, |
| 151 | @ adds <PAlo>, <VA>, <reg>, lsl |
| 152 | @ mov <PAhi>, |
| 153 | @ adc <PAhi>, <PAhi>, |
| 154 | @
|
| 155 | @ In the non-LPAE case, all patchable instructions are ADD or SUB
|
| 156 | @ instructions, where we need to patch in the offset into the
|
| 157 | @ immediate field of the opcode, which is emitted with the correct
|
| 158 | @ rotation value. (The effective value of the immediate is imm12<7:0>
|
| 159 | @ rotated right by [2 * imm12<11:8>] bits)
|
| 160 | @
|
| 161 | @ 31 28 27 23 22 20 19 16 15 12 11 0
|
| 162 | @ +------+-----------------+------+------+-------+
|
| 163 | @ ADD | cond | 0 0 1 0 1 0 0 0 | Rn | Rd | imm12 |
|
| 164 | @ SUB | cond | 0 0 1 0 0 1 0 0 | Rn | Rd | imm12 |
|
| 165 | @ MOV | cond | 0 0 1 1 1 0 1 0 | Rn | Rd | imm12 |
|
| 166 | @ MVN | cond | 0 0 1 1 1 1 1 0 | Rn | Rd | imm12 |
|
| 167 | @ +------+-----------------+------+------+-------+
|
| 168 | @
|
| 169 | @ In the LPAE case, we use a MOVW instruction to carry the low offset
|
| 170 | @ word, and patch in the high word of the offset into the immediate
|
| 171 | @ field of the subsequent MOV instruction, or patch it to a MVN
|
| 172 | @ instruction if the offset is negative. We can distinguish MOVW
|
| 173 | @ instructions based on bits 23:22 of the opcode, and ADD/SUB can be
|
| 174 | @ distinguished from MOV/MVN (all using the encodings above) using
|
| 175 | @ bit 24.
|
| 176 | @
|
| 177 | @ 31 28 27 23 22 20 19 16 15 12 11 0
|
| 178 | @ +------+-----------------+------+------+-------+
|
| 179 | @ MOVW | cond | 0 0 1 1 0 0 0 0 | imm4 | Rd | imm12 |
|
| 180 | @ +------+-----------------+------+------+-------+
|
| 181 | @
|
| 182 | moveq r0, |
| 183 | mov r3, r6, lsr |
| 184 | mov r6, r6, lsr |
| 185 | and r3, r3, |
| 186 | b .Lnext
|
| 187 | .Lloop: ldr ip, [r7, r4]
|
| 188 | |
| 189 | tst ip, |
| 190 | beq 1f
|
| 191 | ARM_BE8(rev ip, ip)
|
| 192 | tst ip, |
| 193 | bic ip, ip, |
| 194 | bfc ip, |
| 195 | orreq ip, ip, r6, lsl |
| 196 | orreq ip, ip, r3, lsr |
| 197 | orrne ip, ip, r0 @ MOV -> mask in offset bits 7-0 (or bit 22)
|
| 198 | ARM_BE8(rev ip, ip)
|
| 199 | b 2f
|
| 200 | 1:
|
| 201 | |
| 202 | tst ip, |
| 203 | bic ip, ip, |
| 204 | orreq ip, ip, r6 ARM_BE8(, lsl |
| 205 | orrne ip, ip, r3 ARM_BE8(, lsl |
| 206 | 2:
|
| 207 | str ip, [r7, r4]
|
| 208 | add r4, r4, |
| 209 | |
| 210 |
|
| 211 | .Lnext:
|
| 212 | cmp r4, r5
|
| 213 | ldrcc r7, [r4] @ use branch for delay slot
|
| 214 | bcc .Lloop
|
| 215 | ret lr
|
| 216 | ENDPROC(__fixup_a_pv_table)
|
| 217 |
|
| 218 | ENTRY(fixup_pv_table)
|
| 219 | stmfd sp!, {r4 - r7, lr}
|
| 220 | mov r4, r0 @ r0 = table start
|
| 221 | add r5, r0, r1 @ r1 = table size
|
| 222 | bl __fixup_a_pv_table
|
| 223 | ldmfd sp!, {r4 - r7, pc}
|
| 224 | ENDPROC(fixup_pv_table)
|
| 225 |
|
| 226 | .data
|
| 227 | .align 2
|
| 228 | .globl __pv_phys_pfn_offset
|
| 229 | .type __pv_phys_pfn_offset, %object
|
| 230 | __pv_phys_pfn_offset:
|
| 231 | .word 0
|
| 232 | .size __pv_phys_pfn_offset, . -__pv_phys_pfn_offset
|
| 233 |
|
| 234 | .globl __pv_offset
|
| 235 | .type __pv_offset, %object
|
| 236 | __pv_offset:
|
| 237 | .quad 0
|
| 238 | .size __pv_offset, . -__pv_offset
|