0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/init.h>
0009 #include <linux/linkage.h>
0010 #include <asm/assembler.h>
0011 #include <asm/page.h>
0012
0013 #ifdef __ARMEB__
0014 #define LOW_OFFSET 0x4
0015 #define HIGH_OFFSET 0x0
0016 #else
0017 #define LOW_OFFSET 0x0
0018 #define HIGH_OFFSET 0x4
0019 #endif
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030 __HEAD
0031 ENTRY(__fixup_pv_table)
0032 mov r0, r8, lsr #PAGE_SHIFT @ convert to PFN
0033 str_l r0, __pv_phys_pfn_offset, r3
0034
0035 adr_l r0, __pv_offset
0036 subs r3, r8, #PAGE_OFFSET @ PHYS_OFFSET - PAGE_OFFSET
0037 mvn ip, #0
0038 strcc ip, [r0, #HIGH_OFFSET] @ save to __pv_offset high bits
0039 str r3, [r0, #LOW_OFFSET] @ save to __pv_offset low bits
0040
0041 mov r0, r3, lsr #21 @ constant for add/sub instructions
0042 teq r3, r0, lsl #21 @ must be 2 MiB aligned
0043 bne 0f
0044
0045 adr_l r4, __pv_table_begin
0046 adr_l r5, __pv_table_end
0047 b __fixup_a_pv_table
0048
0049 0: mov r0, r0 @ deadloop on error
0050 b 0b
0051 ENDPROC(__fixup_pv_table)
0052
0053 .text
0054 __fixup_a_pv_table:
0055 adr_l r6, __pv_offset
0056 ldr r0, [r6, #HIGH_OFFSET] @ pv_offset high word
0057 ldr r6, [r6, #LOW_OFFSET] @ pv_offset low word
0058 cmn r0, #1
0059 #ifdef CONFIG_THUMB2_KERNEL
0060 @
0061 @ The Thumb-2 versions of the patchable sequences are
0062 @
0063 @ phys-to-virt: movw <reg>, #offset<31:21>
0064 @ lsl <reg>, #21
0065 @ sub <VA>, <PA>, <reg>
0066 @
0067 @ virt-to-phys (non-LPAE): movw <reg>, #offset<31:21>
0068 @ lsl <reg>, #21
0069 @ add <PA>, <VA>, <reg>
0070 @
0071 @ virt-to-phys (LPAE): movw <reg>, #offset<31:21>
0072 @ lsl <reg>, #21
0073 @ adds <PAlo>, <VA>, <reg>
0074 @ mov <PAhi>, #offset<39:32>
0075 @ adc <PAhi>, <PAhi>, #0
0076 @
0077 @ In the non-LPAE case, all patchable instructions are MOVW
0078 @ instructions, where we need to patch in the offset into the
0079 @ second halfword of the opcode (the 16-bit immediate is encoded
0080 @ as imm4:i:imm3:imm8)
0081 @
0082 @ 15 11 10 9 4 3 0 15 14 12 11 8 7 0
0083 @ +-----------+---+-------------+------++---+------+----+------+
0084 @ MOVW | 1 1 1 1 0 | i | 1 0 0 1 0 0 | imm4 || 0 | imm3 | Rd | imm8 |
0085 @ +-----------+---+-------------+------++---+------+----+------+
0086 @
0087 @ In the LPAE case, we also need to patch in the high word of the
0088 @ offset into the immediate field of the MOV instruction, or patch it
0089 @ to a MVN instruction if the offset is negative. In this case, we
0090 @ need to inspect the first halfword of the opcode, to check whether
0091 @ it is MOVW or MOV/MVN, and to perform the MOV to MVN patching if
0092 @ needed. The encoding of the immediate is rather complex for values
0093 @ of i:imm3 != 0b0000, but fortunately, we never need more than 8 lower
0094 @ order bits, which can be patched into imm8 directly (and i:imm3
0095 @ cleared)
0096 @
0097 @ 15 11 10 9 5 0 15 14 12 11 8 7 0
0098 @ +-----------+---+---------------------++---+------+----+------+
0099 @ MOV | 1 1 1 1 0 | i | 0 0 0 1 0 0 1 1 1 1 || 0 | imm3 | Rd | imm8 |
0100 @ MVN | 1 1 1 1 0 | i | 0 0 0 1 1 0 1 1 1 1 || 0 | imm3 | Rd | imm8 |
0101 @ +-----------+---+---------------------++---+------+----+------+
0102 @
0103 moveq r0, #0x200000 @ set bit 21, mov to mvn instruction
0104 lsrs r3, r6, #29 @ isolate top 3 bits of displacement
0105 ubfx r6, r6, #21, #8 @ put bits 28:21 into the MOVW imm8 field
0106 bfi r6, r3, #12, #3 @ put bits 31:29 into the MOVW imm3 field
0107 b .Lnext
0108 .Lloop: add r7, r4
0109 adds r4, #4 @ clears Z flag
0110 #ifdef CONFIG_ARM_LPAE
0111 ldrh ip, [r7]
0112 ARM_BE8(rev16 ip, ip)
0113 tst ip, #0x200 @ MOVW has bit 9 set, MVN has it clear
0114 bne 0f @ skip to MOVW handling (Z flag is clear)
0115 bic ip, #0x20 @ clear bit 5 (MVN -> MOV)
0116 orr ip, ip, r0, lsr #16 @ MOV -> MVN if offset < 0
0117 ARM_BE8(rev16 ip, ip)
0118 strh ip, [r7]
0119 @ Z flag is set
0120 0:
0121 #endif
0122 ldrh ip, [r7, #2]
0123 ARM_BE8(rev16 ip, ip)
0124 and ip, #0xf00 @ clear everything except Rd field
0125 orreq ip, r0 @ Z flag set -> MOV/MVN -> patch in high bits
0126 orrne ip, r6 @ Z flag clear -> MOVW -> patch in low bits
0127 ARM_BE8(rev16 ip, ip)
0128 strh ip, [r7, #2]
0129 #else
0130 #ifdef CONFIG_CPU_ENDIAN_BE8
0131 @ in BE8, we load data in BE, but instructions still in LE
0132 #define PV_BIT24 0x00000001
0133 #define PV_IMM8_MASK 0xff000000
0134 #define PV_IMMR_MSB 0x00080000
0135 #else
0136 #define PV_BIT24 0x01000000
0137 #define PV_IMM8_MASK 0x000000ff
0138 #define PV_IMMR_MSB 0x00000800
0139 #endif
0140
0141 @
0142 @ The ARM versions of the patchable sequences are
0143 @
0144 @ phys-to-virt: sub <VA>, <PA>, #offset<31:24>, lsl #24
0145 @ sub <VA>, <PA>, #offset<23:16>, lsl #16
0146 @
0147 @ virt-to-phys (non-LPAE): add <PA>, <VA>, #offset<31:24>, lsl #24
0148 @ add <PA>, <VA>, #offset<23:16>, lsl #16
0149 @
0150 @ virt-to-phys (LPAE): movw <reg>, #offset<31:20>
0151 @ adds <PAlo>, <VA>, <reg>, lsl #20
0152 @ mov <PAhi>, #offset<39:32>
0153 @ adc <PAhi>, <PAhi>, #0
0154 @
0155 @ In the non-LPAE case, all patchable instructions are ADD or SUB
0156 @ instructions, where we need to patch in the offset into the
0157 @ immediate field of the opcode, which is emitted with the correct
0158 @ rotation value. (The effective value of the immediate is imm12<7:0>
0159 @ rotated right by [2 * imm12<11:8>] bits)
0160 @
0161 @ 31 28 27 23 22 20 19 16 15 12 11 0
0162 @ +------+-----------------+------+------+-------+
0163 @ ADD | cond | 0 0 1 0 1 0 0 0 | Rn | Rd | imm12 |
0164 @ SUB | cond | 0 0 1 0 0 1 0 0 | Rn | Rd | imm12 |
0165 @ MOV | cond | 0 0 1 1 1 0 1 0 | Rn | Rd | imm12 |
0166 @ MVN | cond | 0 0 1 1 1 1 1 0 | Rn | Rd | imm12 |
0167 @ +------+-----------------+------+------+-------+
0168 @
0169 @ In the LPAE case, we use a MOVW instruction to carry the low offset
0170 @ word, and patch in the high word of the offset into the immediate
0171 @ field of the subsequent MOV instruction, or patch it to a MVN
0172 @ instruction if the offset is negative. We can distinguish MOVW
0173 @ instructions based on bits 23:22 of the opcode, and ADD/SUB can be
0174 @ distinguished from MOV/MVN (all using the encodings above) using
0175 @ bit 24.
0176 @
0177 @ 31 28 27 23 22 20 19 16 15 12 11 0
0178 @ +------+-----------------+------+------+-------+
0179 @ MOVW | cond | 0 0 1 1 0 0 0 0 | imm4 | Rd | imm12 |
0180 @ +------+-----------------+------+------+-------+
0181 @
0182 moveq r0, #0x400000 @ set bit 22, mov to mvn instruction
0183 mov r3, r6, lsr #16 @ put offset bits 31-16 into r3
0184 mov r6, r6, lsr #24 @ put offset bits 31-24 into r6
0185 and r3, r3, #0xf0 @ only keep offset bits 23-20 in r3
0186 b .Lnext
0187 .Lloop: ldr ip, [r7, r4]
0188 #ifdef CONFIG_ARM_LPAE
0189 tst ip, #PV_BIT24 @ ADD/SUB have bit 24 clear
0190 beq 1f
0191 ARM_BE8(rev ip, ip)
0192 tst ip, #0xc00000 @ MOVW has bits 23:22 clear
0193 bic ip, ip, #0x400000 @ clear bit 22
0194 bfc ip, #0, #12 @ clear imm12 field of MOV[W] instruction
0195 orreq ip, ip, r6, lsl #4 @ MOVW -> mask in offset bits 31-24
0196 orreq ip, ip, r3, lsr #4 @ MOVW -> mask in offset bits 23-20
0197 orrne ip, ip, r0 @ MOV -> mask in offset bits 7-0 (or bit 22)
0198 ARM_BE8(rev ip, ip)
0199 b 2f
0200 1:
0201 #endif
0202 tst ip, #PV_IMMR_MSB @ rotation value >= 16 ?
0203 bic ip, ip, #PV_IMM8_MASK
0204 orreq ip, ip, r6 ARM_BE8(, lsl #24) @ mask in offset bits 31-24
0205 orrne ip, ip, r3 ARM_BE8(, lsl #24) @ mask in offset bits 23-20
0206 2:
0207 str ip, [r7, r4]
0208 add r4, r4, #4
0209 #endif
0210
0211 .Lnext:
0212 cmp r4, r5
0213 ldrcc r7, [r4] @ use branch for delay slot
0214 bcc .Lloop
0215 ret lr
0216 ENDPROC(__fixup_a_pv_table)
0217
0218 ENTRY(fixup_pv_table)
0219 stmfd sp!, {r4 - r7, lr}
0220 mov r4, r0 @ r0 = table start
0221 add r5, r0, r1 @ r1 = table size
0222 bl __fixup_a_pv_table
0223 ldmfd sp!, {r4 - r7, pc}
0224 ENDPROC(fixup_pv_table)
0225
0226 .data
0227 .align 2
0228 .globl __pv_phys_pfn_offset
0229 .type __pv_phys_pfn_offset, %object
0230 __pv_phys_pfn_offset:
0231 .word 0
0232 .size __pv_phys_pfn_offset, . -__pv_phys_pfn_offset
0233
0234 .globl __pv_offset
0235 .type __pv_offset, %object
0236 __pv_offset:
0237 .quad 0
0238 .size __pv_offset, . -__pv_offset