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 #include <linux/init.h> 9 #include <linux/linkage.h> 10 #include <asm/assembler.h> 11 #include <asm/page.h> 12 13 #ifdef __ARMEB__ 14 #define LOW_OFFSET 0x4 15 #define HIGH_OFFSET 0x0 16 #else 17 #define LOW_OFFSET 0x0 18 #define HIGH_OFFSET 0x4 19 #endif 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 #PAGE_SHIFT @ convert to PFN 33 str_l r0, __pv_phys_pfn_offset, r3 34 35 adr_l r0, __pv_offset 36 subs r3, r8, #PAGE_OFFSET @ PHYS_OFFSET - PAGE_OFFSET 37 mvn ip, #0 38 strcc ip, [r0, #HIGH_OFFSET] @ save to __pv_offset high bits 39 str r3, [r0, #LOW_OFFSET] @ save to __pv_offset low bits 40 41 mov r0, r3, lsr #21 @ constant for add/sub instructions 42 teq r3, r0, lsl #21 @ must be 2 MiB aligned 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, #HIGH_OFFSET] @ pv_offset high word 57 ldr r6, [r6, #LOW_OFFSET] @ pv_offset low word 58 cmn r0, #1 59 #ifdef CONFIG_THUMB2_KERNEL 60 @ 61 @ The Thumb-2 versions of the patchable sequences are 62 @ 63 @ phys-to-virt: movw <reg>, #offset<31:21> 64 @ lsl <reg>, #21 65 @ sub <VA>, <PA>, <reg> 66 @ 67 @ virt-to-phys (non-LPAE): movw <reg>, #offset<31:21> 68 @ lsl <reg>, #21 69 @ add <PA>, <VA>, <reg> 70 @ 71 @ virt-to-phys (LPAE): movw <reg>, #offset<31:21> 72 @ lsl <reg>, #21 73 @ adds <PAlo>, <VA>, <reg> 74 @ mov <PAhi>, #offset<39:32> 75 @ adc <PAhi>, <PAhi>, #0 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, #0x200000 @ set bit 21, mov to mvn instruction 104 lsrs r3, r6, #29 @ isolate top 3 bits of displacement 105 ubfx r6, r6, #21, #8 @ put bits 28:21 into the MOVW imm8 field 106 bfi r6, r3, #12, #3 @ put bits 31:29 into the MOVW imm3 field 107 b .Lnext 108 .Lloop: add r7, r4 109 adds r4, #4 @ clears Z flag 110 #ifdef CONFIG_ARM_LPAE 111 ldrh ip, [r7] 112 ARM_BE8(rev16 ip, ip) 113 tst ip, #0x200 @ MOVW has bit 9 set, MVN has it clear 114 bne 0f @ skip to MOVW handling (Z flag is clear) 115 bic ip, #0x20 @ clear bit 5 (MVN -> MOV) 116 orr ip, ip, r0, lsr #16 @ MOV -> MVN if offset < 0 117 ARM_BE8(rev16 ip, ip) 118 strh ip, [r7] 119 @ Z flag is set 120 0: 121 #endif 122 ldrh ip, [r7, #2] 123 ARM_BE8(rev16 ip, ip) 124 and ip, #0xf00 @ clear everything except Rd field 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, #2] 129 #else 130 #ifdef CONFIG_CPU_ENDIAN_BE8 131 @ in BE8, we load data in BE, but instructions still in LE 132 #define PV_BIT24 0x00000001 133 #define PV_IMM8_MASK 0xff000000 134 #define PV_IMMR_MSB 0x00080000 135 #else 136 #define PV_BIT24 0x01000000 137 #define PV_IMM8_MASK 0x000000ff 138 #define PV_IMMR_MSB 0x00000800 139 #endif 140 141 @ 142 @ The ARM versions of the patchable sequences are 143 @ 144 @ phys-to-virt: sub <VA>, <PA>, #offset<31:24>, lsl #24 145 @ sub <VA>, <PA>, #offset<23:16>, lsl #16 146 @ 147 @ virt-to-phys (non-LPAE): add <PA>, <VA>, #offset<31:24>, lsl #24 148 @ add <PA>, <VA>, #offset<23:16>, lsl #16 149 @ 150 @ virt-to-phys (LPAE): movw <reg>, #offset<31:20> 151 @ adds <PAlo>, <VA>, <reg>, lsl #20 152 @ mov <PAhi>, #offset<39:32> 153 @ adc <PAhi>, <PAhi>, #0 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, #0x400000 @ set bit 22, mov to mvn instruction 183 mov r3, r6, lsr #16 @ put offset bits 31-16 into r3 184 mov r6, r6, lsr #24 @ put offset bits 31-24 into r6 185 and r3, r3, #0xf0 @ only keep offset bits 23-20 in r3 186 b .Lnext 187 .Lloop: ldr ip, [r7, r4] 188 #ifdef CONFIG_ARM_LPAE 189 tst ip, #PV_BIT24 @ ADD/SUB have bit 24 clear 190 beq 1f 191 ARM_BE8(rev ip, ip) 192 tst ip, #0xc00000 @ MOVW has bits 23:22 clear 193 bic ip, ip, #0x400000 @ clear bit 22 194 bfc ip, #0, #12 @ clear imm12 field of MOV[W] instruction 195 orreq ip, ip, r6, lsl #4 @ MOVW -> mask in offset bits 31-24 196 orreq ip, ip, r3, lsr #4 @ MOVW -> mask in offset bits 23-20 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 #endif 202 tst ip, #PV_IMMR_MSB @ rotation value >= 16 ? 203 bic ip, ip, #PV_IMM8_MASK 204 orreq ip, ip, r6 ARM_BE8(, lsl #24) @ mask in offset bits 31-24 205 orrne ip, ip, r3 ARM_BE8(, lsl #24) @ mask in offset bits 23-20 206 2: 207 str ip, [r7, r4] 208 add r4, r4, #4 209 #endif 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.