| 1 | /* SPDX-License-Identifier: GPL-2.0-only */
|
| 2 | /*
|
| 3 | * Event entry/exit for Hexagon
|
| 4 | *
|
| 5 | * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
|
| 6 | */
|
| 7 |
|
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 |
|
| 13 | /*
|
| 14 | * Entry into guest-mode Linux under Hexagon Virtual Machine.
|
| 15 | * Stack pointer points to event record - build pt_regs on top of it,
|
| 16 | * set up a plausible C stack frame, and dispatch to the C handler.
|
| 17 | * On return, do vmrte virtual instruction with SP where we started.
|
| 18 | *
|
| 19 | * VM Spec 0.5 uses a trap to fetch HVM record now.
|
| 20 | */
|
| 21 |
|
| 22 | /*
|
| 23 | * Save full register state, while setting up thread_info struct
|
| 24 | * pointer derived from kernel stack pointer in THREADINFO_REG
|
| 25 | * register, putting prior thread_info.regs pointer in a callee-save
|
| 26 | * register (R24, which had better not ever be assigned to THREADINFO_REG),
|
| 27 | * and updating thread_info.regs to point to current stack frame,
|
| 28 | * so as to support nested events in kernel mode.
|
| 29 | *
|
| 30 | * As this is common code, we set the pt_regs system call number
|
| 31 | * to -1 for all events. It will be replaced with the system call
|
| 32 | * number in the case where we decode a system call (trap0( |
| 33 | */
|
| 34 |
|
| 35 | |
| 36 | |
| 37 | memd(R0 + |
| 38 | { memw(R0 + |
| 39 | R31 = memw(R0 + |
| 40 | { memw(R0 + |
| 41 | R31 = ugp; } \
|
| 42 | { memd(R0 + |
| 43 | R30 = gp ; } \
|
| 44 | memd(R0 + |
| 45 | memd(R0 + |
| 46 | memd(R0 + |
| 47 | memd(R0 + |
| 48 | memd(R0 + |
| 49 | memd(R0 + |
| 50 | memd(R0 + |
| 51 | { memd(R0 + |
| 52 | R15 = lc0; } \
|
| 53 | { memd(R0 + |
| 54 | R14 = sa0; } \
|
| 55 | { memd(R0 + |
| 56 | R13 = lc1; } \
|
| 57 | { memd(R0 + |
| 58 | R12 = sa1; } \
|
| 59 | { memd(R0 + |
| 60 | R11 = m1; \
|
| 61 | R2.H = |
| 62 | { memd(R0 + |
| 63 | R10 = m0; \
|
| 64 | R2.L = |
| 65 | { memd(R0 + |
| 66 | R15 = p3:0; \
|
| 67 | R2 = neg(R2); } \
|
| 68 | { memd(R0 + |
| 69 | R14 = usr; \
|
| 70 | R2 = and(R0,R2); } \
|
| 71 | { memd(R0 + |
| 72 | THREADINFO_REG = R2; } \
|
| 73 | { r24 = memw(THREADINFO_REG + |
| 74 | memw(THREADINFO_REG + |
| 75 | R2 = |
| 76 | { memw(R0 + |
| 77 | R30 = |
| 78 | |
| 79 | /* V4+ */
|
| 80 | /* the |
| 81 | |
| 82 | { memd(R0 + |
| 83 | R30 = memw(R0 + |
| 84 | { memw(R0 + |
| 85 | memw(R0 + |
| 86 | { R31:30 = C11:10; \
|
| 87 | memd(R0 + |
| 88 | memd(R0 + |
| 89 | { memd(R0 + |
| 90 | memd(R0 + |
| 91 | { memd(R0 + |
| 92 | memd(R0 + |
| 93 | { memd(R0 + |
| 94 | memd(R0 + |
| 95 | R17:16 = C13:12; }\
|
| 96 | { memd(R0 + |
| 97 | memd(R0 + |
| 98 | R15:14 = C1:0; } \
|
| 99 | { memd(R0 + |
| 100 | memd(R0 + |
| 101 | R13:12 = C3:2; } \
|
| 102 | { memd(R0 + |
| 103 | memd(R0 + |
| 104 | R11:10 = C7:6; }\
|
| 105 | { THREADINFO_REG = and(R0, |
| 106 | memd(R0 + |
| 107 | R15 = p3:0; }\
|
| 108 | { memd(R0 + |
| 109 | memw(R0 + |
| 110 | { r24 = memw(THREADINFO_REG + |
| 111 | memw(THREADINFO_REG + |
| 112 | R2 = |
| 113 | { memw(R0 + #_PT_SYSCALL_NR) = R2; \
|
| 114 | memd(R0 + #_PT_CS1CS0) = R17:16; \
|
| 115 | R30 = #0; }
|
| 116 | |
| 117 |
|
| 118 | /*
|
| 119 | * Restore registers and thread_info.regs state. THREADINFO_REG
|
| 120 | * is assumed to still be sane, and R24 to have been correctly
|
| 121 | * preserved. Don't restore R29 (SP) until later.
|
| 122 | */
|
| 123 |
|
| 124 | #if CONFIG_HEXAGON_ARCH_VERSION < 4
|
| 125 | #define restore_pt_regs() \
|
| 126 | { memw(THREADINFO_REG + #_THREAD_INFO_PT_REGS) = R24; \
|
| 127 | R15:14 = memd(R0 + #_PT_PREDSUSR); } \
|
| 128 | { R11:10 = memd(R0 + #_PT_M1M0); \
|
| 129 | p3:0 = R15; } \
|
| 130 | { R13:12 = memd(R0 + #_PT_LC1SA1); \
|
| 131 | usr = R14; } \
|
| 132 | { R15:14 = memd(R0 + #_PT_LC0SA0); \
|
| 133 | m1 = R11; } \
|
| 134 | { R3:2 = memd(R0 + #_PT_R0302); \
|
| 135 | m0 = R10; } \
|
| 136 | { R5:4 = memd(R0 + #_PT_R0504); \
|
| 137 | lc1 = R13; } \
|
| 138 | { R7:6 = memd(R0 + #_PT_R0706); \
|
| 139 | sa1 = R12; } \
|
| 140 | { R9:8 = memd(R0 + #_PT_R0908); \
|
| 141 | lc0 = R15; } \
|
| 142 | { R11:10 = memd(R0 + #_PT_R1110); \
|
| 143 | sa0 = R14; } \
|
| 144 | { R13:12 = memd(R0 + #_PT_R1312); \
|
| 145 | R15:14 = memd(R0 + #_PT_R1514); } \
|
| 146 | { R17:16 = memd(R0 + #_PT_R1716); \
|
| 147 | R19:18 = memd(R0 + #_PT_R1918); } \
|
| 148 | { R21:20 = memd(R0 + #_PT_R2120); \
|
| 149 | R23:22 = memd(R0 + #_PT_R2322); } \
|
| 150 | { R25:24 = memd(R0 + #_PT_R2524); \
|
| 151 | R27:26 = memd(R0 + #_PT_R2726); } \
|
| 152 | R31:30 = memd(R0 + #_PT_GPUGP); \
|
| 153 | { R28 = memw(R0 + #_PT_R2928); \
|
| 154 | ugp = R31; } \
|
| 155 | { R31:30 = memd(R0 + #_PT_R3130); \
|
| 156 | gp = R30; }
|
| 157 | #else
|
| 158 | /* V4+ */
|
| 159 | #define restore_pt_regs() \
|
| 160 | { memw(THREADINFO_REG + #_THREAD_INFO_PT_REGS) = R24; \
|
| 161 | R15:14 = memd(R0 + #_PT_PREDSUSR); } \
|
| 162 | { R11:10 = memd(R0 + #_PT_M1M0); \
|
| 163 | R13:12 = memd(R0 + #_PT_LC1SA1); \
|
| 164 | p3:0 = R15; } \
|
| 165 | { R15:14 = memd(R0 + #_PT_LC0SA0); \
|
| 166 | R3:2 = memd(R0 + #_PT_R0302); \
|
| 167 | usr = R14; } \
|
| 168 | { R5:4 = memd(R0 + #_PT_R0504); \
|
| 169 | R7:6 = memd(R0 + #_PT_R0706); \
|
| 170 | C7:6 = R11:10; }\
|
| 171 | { R9:8 = memd(R0 + #_PT_R0908); \
|
| 172 | R11:10 = memd(R0 + #_PT_R1110); \
|
| 173 | C3:2 = R13:12; }\
|
| 174 | { R13:12 = memd(R0 + #_PT_R1312); \
|
| 175 | R15:14 = memd(R0 + #_PT_R1514); \
|
| 176 | C1:0 = R15:14; }\
|
| 177 | { R17:16 = memd(R0 + #_PT_R1716); \
|
| 178 | R19:18 = memd(R0 + #_PT_R1918); } \
|
| 179 | { R21:20 = memd(R0 + #_PT_R2120); \
|
| 180 | R23:22 = memd(R0 + #_PT_R2322); } \
|
| 181 | { R25:24 = memd(R0 + #_PT_R2524); \
|
| 182 | R27:26 = memd(R0 + #_PT_R2726); } \
|
| 183 | R31:30 = memd(R0 + #_PT_CS1CS0); \
|
| 184 | { C13:12 = R31:30; \
|
| 185 | R31:30 = memd(R0 + #_PT_GPUGP) ; \
|
| 186 | R28 = memw(R0 + #_PT_R2928); }\
|
| 187 | { C11:10 = R31:30; \
|
| 188 | R31:30 = memd(R0 + #_PT_R3130); }
|
| 189 | #endif
|
| 190 |
|
| 191 | /*
|
| 192 | * Clears off enough space for the rest of pt_regs; evrec is a part
|
| 193 | * of pt_regs in HVM mode. Save R0/R1, set handler's address in R1.
|
| 194 | * R0 is the address of pt_regs and is the parameter to save_pt_regs.
|
| 195 | */
|
| 196 |
|
| 197 | /*
|
| 198 | * Since the HVM isn't automagically pushing the EVREC onto the stack anymore,
|
| 199 | * we'll subract the entire size out and then fill it in ourselves.
|
| 200 | * Need to save off R0, R1, R2, R3 immediately.
|
| 201 | */
|
| 202 |
|
| 203 | |
| 204 | |
| 205 | { \
|
| 206 | R29 = add(R29, #-(_PT_REGS_SIZE)); \
|
| 207 | memd(R29 + #(_PT_R0100 + -_PT_REGS_SIZE)) = R1:0; \
|
| 208 | } \
|
| 209 | { \
|
| 210 | memd(R29 +#_PT_R0302) = R3:2; \
|
| 211 | } \
|
| 212 | trap1(#HVM_TRAP1_VMGETREGS); \
|
| 213 | { \
|
| 214 | memd(R29 + #_PT_ER_VMEL) = R1:0; \
|
| 215 | R0 = R29; \
|
| 216 | R1.L = #LO(CHandler); \
|
| 217 | } \
|
| 218 | { \
|
| 219 | memd(R29 + #_PT_ER_VMPSP) = R3:2; \
|
| 220 | R1.H = #HI(CHandler); \
|
| 221 | jump event_dispatch; \
|
| 222 | }
|
| 223 | |
| 224 | /* V4+ */
|
| 225 | /* turn on I$ prefetch early */
|
| 226 | /* the |
| 227 | |
| 228 | { \
|
| 229 | R29 = add(R29, #-(_PT_REGS_SIZE)); \
|
| 230 | memd(R29 + #(_PT_R0100 + -_PT_REGS_SIZE)) = R1:0; \
|
| 231 | memd(R29 + #(_PT_R0302 + -_PT_REGS_SIZE)) = R3:2; \
|
| 232 | R0 = usr; \
|
| 233 | } \
|
| 234 | { \
|
| 235 | memw(R29 + #_PT_PREDSUSR) = R0; \
|
| 236 | R0 = setbit(R0, #16); \
|
| 237 | } \
|
| 238 | usr = R0; \
|
| 239 | R1:0 = G1:0; \
|
| 240 | { \
|
| 241 | memd(R29 + #_PT_ER_VMEL) = R1:0; \
|
| 242 | R1 = # ## #(CHandler); \
|
| 243 | R3:2 = G3:2; \
|
| 244 | } \
|
| 245 | { \
|
| 246 | R0 = R29; \
|
| 247 | memd(R29 + #_PT_ER_VMPSP) = R3:2; \
|
| 248 | jump event_dispatch; \
|
| 249 | }
|
| 250 | |
| 251 |
|
| 252 | .text
|
| 253 | /*
|
| 254 | * Do bulk save/restore in one place.
|
| 255 | * Adds a jump to dispatch latency, but
|
| 256 | * saves hundreds of bytes.
|
| 257 | */
|
| 258 |
|
| 259 | event_dispatch:
|
| 260 | save_pt_regs()
|
| 261 | callr r1
|
| 262 |
|
| 263 | /*
|
| 264 | * Coming back from the C-world, our thread info pointer
|
| 265 | * should be in the designated register (usually R19)
|
| 266 | *
|
| 267 | * If we were in kernel mode, we don't need to check scheduler
|
| 268 | * or signals if CONFIG_PREEMPTION is not set. If set, then it has
|
| 269 | * to jump to a need_resched kind of block.
|
| 270 | * BTW, CONFIG_PREEMPTION is not supported yet.
|
| 271 | */
|
| 272 |
|
| 273 | #ifdef CONFIG_PREEMPTION
|
| 274 | R0 = #VM_INT_DISABLE
|
| 275 | trap1(#HVM_TRAP1_VMSETIE)
|
| 276 | #endif
|
| 277 |
|
| 278 | /* "Nested control path" -- if the previous mode was kernel */
|
| 279 | {
|
| 280 | R0 = memw(R29 + #_PT_ER_VMEST);
|
| 281 | R26.L = #LO(do_work_pending);
|
| 282 | }
|
| 283 | {
|
| 284 | P0 = tstbit(R0, #HVM_VMEST_UM_SFT);
|
| 285 | if (!P0.new) jump:nt restore_all;
|
| 286 | R26.H = #HI(do_work_pending);
|
| 287 | R0 = #VM_INT_DISABLE;
|
| 288 | }
|
| 289 |
|
| 290 | /*
|
| 291 | * Check also the return from fork/system call, normally coming back from
|
| 292 | * user mode
|
| 293 | *
|
| 294 | * R26 needs to have do_work_pending, and R0 should have VM_INT_DISABLE
|
| 295 | */
|
| 296 |
|
| 297 | check_work_pending:
|
| 298 | /* Disable interrupts while checking TIF */
|
| 299 | trap1(#HVM_TRAP1_VMSETIE)
|
| 300 | {
|
| 301 | R0 = R29; /* regs should still be at top of stack */
|
| 302 | R1 = memw(THREADINFO_REG + #_THREAD_INFO_FLAGS);
|
| 303 | callr R26;
|
| 304 | }
|
| 305 |
|
| 306 | {
|
| 307 | P0 = cmp.eq(R0, #0); if (!P0.new) jump:nt check_work_pending;
|
| 308 | R0 = #VM_INT_DISABLE;
|
| 309 | }
|
| 310 |
|
| 311 | restore_all:
|
| 312 | /*
|
| 313 | * Disable interrupts, if they weren't already, before reg restore.
|
| 314 | * R0 gets preloaded with |
| 315 | */
|
| 316 | trap1(#HVM_TRAP1_VMSETIE)
|
| 317 |
|
| 318 | /* do the setregs here for VM 0.5 */
|
| 319 | /* R29 here should already be pointing at pt_regs */
|
| 320 | {
|
| 321 | R1:0 = memd(R29 + #_PT_ER_VMEL);
|
| 322 | R3:2 = memd(R29 + #_PT_ER_VMPSP);
|
| 323 | }
|
| 324 | #if CONFIG_HEXAGON_ARCH_VERSION < 4
|
| 325 | trap1(#HVM_TRAP1_VMSETREGS);
|
| 326 | |
| 327 | G1:0 = R1:0;
|
| 328 | G3:2 = R3:2;
|
| 329 | |
| 330 |
|
| 331 | R0 = R29
|
| 332 | restore_pt_regs()
|
| 333 | {
|
| 334 | R1:0 = memd(R29 + #_PT_R0100);
|
| 335 | R29 = add(R29, #_PT_REGS_SIZE);
|
| 336 | }
|
| 337 | trap1(#HVM_TRAP1_VMRTE)
|
| 338 | /* Notreached */
|
| 339 |
|
| 340 |
|
| 341 | .globl _K_enter_genex
|
| 342 | _K_enter_genex:
|
| 343 | vm_event_entry(do_genex)
|
| 344 |
|
| 345 | .globl _K_enter_interrupt
|
| 346 | _K_enter_interrupt:
|
| 347 | vm_event_entry(arch_do_IRQ)
|
| 348 |
|
| 349 | .globl _K_enter_trap0
|
| 350 | _K_enter_trap0:
|
| 351 | vm_event_entry(do_trap0)
|
| 352 |
|
| 353 | .globl _K_enter_machcheck
|
| 354 | _K_enter_machcheck:
|
| 355 | vm_event_entry(do_machcheck)
|
| 356 |
|
| 357 | .globl _K_enter_debug
|
| 358 | _K_enter_debug:
|
| 359 | vm_event_entry(do_debug_exception)
|
| 360 |
|
| 361 | .globl ret_from_fork
|
| 362 | ret_from_fork:
|
| 363 | {
|
| 364 | call schedule_tail
|
| 365 | R26.H = #HI(do_work_pending);
|
| 366 | }
|
| 367 | {
|
| 368 | P0 = cmp.eq(R24, #0);
|
| 369 | R26.L = #LO(do_work_pending);
|
| 370 | R0 = #VM_INT_DISABLE;
|
| 371 | }
|
| 372 | if (P0) jump check_work_pending
|
| 373 | {
|
| 374 | R0 = R25;
|
| 375 | callr R24
|
| 376 | }
|
| 377 | {
|
| 378 | jump check_work_pending
|
| 379 | R0 = #VM_INT_DISABLE;
|
| 380 | }
|