blob: 9a18b4939b353922456be556d0ac9326dc39e8ef [file] [log] [blame]
James Hogan90e93112016-06-23 17:34:39 +01001/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Generation of main entry point for the guest, exception handling.
7 *
8 * Copyright (C) 2012 MIPS Technologies, Inc.
9 * Authors: Sanjay Lal <sanjayl@kymasys.com>
10 *
11 * Copyright (C) 2016 Imagination Technologies Ltd.
12 */
13
14#include <linux/kvm_host.h>
15#include <asm/msa.h>
16#include <asm/setup.h>
17#include <asm/uasm.h>
18
19/* Register names */
20#define ZERO 0
21#define AT 1
22#define V0 2
23#define V1 3
24#define A0 4
25#define A1 5
26
27#if _MIPS_SIM == _MIPS_SIM_ABI32
28#define T0 8
29#define T1 9
30#define T2 10
31#define T3 11
32#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
33
34#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
35#define T0 12
36#define T1 13
37#define T2 14
38#define T3 15
39#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 */
40
41#define S0 16
42#define S1 17
43#define T9 25
44#define K0 26
45#define K1 27
46#define GP 28
47#define SP 29
48#define RA 31
49
50/* Some CP0 registers */
51#define C0_HWRENA 7, 0
52#define C0_BADVADDR 8, 0
53#define C0_ENTRYHI 10, 0
54#define C0_STATUS 12, 0
55#define C0_CAUSE 13, 0
56#define C0_EPC 14, 0
57#define C0_EBASE 15, 1
58#define C0_CONFIG3 16, 3
59#define C0_CONFIG5 16, 5
60#define C0_DDATA_LO 28, 3
61#define C0_ERROREPC 30, 0
62
63#define CALLFRAME_SIZ 32
64
65enum label_id {
66 label_fpu_1 = 1,
67 label_msa_1,
68 label_return_to_host,
69 label_kernel_asid,
70};
71
72UASM_L_LA(_fpu_1)
73UASM_L_LA(_msa_1)
74UASM_L_LA(_return_to_host)
75UASM_L_LA(_kernel_asid)
76
77static void *kvm_mips_build_enter_guest(void *addr);
78static void *kvm_mips_build_ret_from_exit(void *addr);
79static void *kvm_mips_build_ret_to_guest(void *addr);
80static void *kvm_mips_build_ret_to_host(void *addr);
81
82/**
83 * kvm_mips_build_vcpu_run() - Assemble function to start running a guest VCPU.
84 * @addr: Address to start writing code.
85 *
86 * Assemble the start of the vcpu_run function to run a guest VCPU. The function
87 * conforms to the following prototype:
88 *
89 * int vcpu_run(struct kvm_run *run, struct kvm_vcpu *vcpu);
90 *
91 * The exit from the guest and return to the caller is handled by the code
92 * generated by kvm_mips_build_ret_to_host().
93 *
94 * Returns: Next address after end of written function.
95 */
96void *kvm_mips_build_vcpu_run(void *addr)
97{
98 u32 *p = addr;
99 unsigned int i;
100
101 /*
102 * A0: run
103 * A1: vcpu
104 */
105
106 /* k0/k1 not being used in host kernel context */
107 uasm_i_addiu(&p, K1, SP, -(int)sizeof(struct pt_regs));
108 for (i = 16; i < 32; ++i) {
109 if (i == 24)
110 i = 28;
111 UASM_i_SW(&p, i, offsetof(struct pt_regs, regs[i]), K1);
112 }
113
114 /* Save hi/lo */
115 uasm_i_mflo(&p, V0);
116 UASM_i_SW(&p, V0, offsetof(struct pt_regs, lo), K1);
117 uasm_i_mfhi(&p, V1);
118 UASM_i_SW(&p, V1, offsetof(struct pt_regs, hi), K1);
119
120 /* Save host status */
121 uasm_i_mfc0(&p, V0, C0_STATUS);
122 UASM_i_SW(&p, V0, offsetof(struct pt_regs, cp0_status), K1);
123
124 /* Save DDATA_LO, will be used to store pointer to vcpu */
125 uasm_i_mfc0(&p, V1, C0_DDATA_LO);
126 UASM_i_SW(&p, V1, offsetof(struct pt_regs, cp0_epc), K1);
127
128 /* DDATA_LO has pointer to vcpu */
129 uasm_i_mtc0(&p, A1, C0_DDATA_LO);
130
131 /* Offset into vcpu->arch */
132 uasm_i_addiu(&p, K1, A1, offsetof(struct kvm_vcpu, arch));
133
134 /*
135 * Save the host stack to VCPU, used for exception processing
136 * when we exit from the Guest
137 */
138 UASM_i_SW(&p, SP, offsetof(struct kvm_vcpu_arch, host_stack), K1);
139
140 /* Save the kernel gp as well */
141 UASM_i_SW(&p, GP, offsetof(struct kvm_vcpu_arch, host_gp), K1);
142
143 /*
144 * Setup status register for running the guest in UM, interrupts
145 * are disabled
146 */
147 UASM_i_LA(&p, K0, ST0_EXL | KSU_USER | ST0_BEV);
148 uasm_i_mtc0(&p, K0, C0_STATUS);
149 uasm_i_ehb(&p);
150
151 /* load up the new EBASE */
152 UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, guest_ebase), K1);
153 uasm_i_mtc0(&p, K0, C0_EBASE);
154
155 /*
156 * Now that the new EBASE has been loaded, unset BEV, set
157 * interrupt mask as it was but make sure that timer interrupts
158 * are enabled
159 */
160 uasm_i_addiu(&p, K0, ZERO, ST0_EXL | KSU_USER | ST0_IE);
161 uasm_i_andi(&p, V0, V0, ST0_IM);
162 uasm_i_or(&p, K0, K0, V0);
163 uasm_i_mtc0(&p, K0, C0_STATUS);
164 uasm_i_ehb(&p);
165
166 p = kvm_mips_build_enter_guest(p);
167
168 return p;
169}
170
171/**
172 * kvm_mips_build_enter_guest() - Assemble code to resume guest execution.
173 * @addr: Address to start writing code.
174 *
175 * Assemble the code to resume guest execution. This code is common between the
176 * initial entry into the guest from the host, and returning from the exit
177 * handler back to the guest.
178 *
179 * Returns: Next address after end of written function.
180 */
181static void *kvm_mips_build_enter_guest(void *addr)
182{
183 u32 *p = addr;
184 unsigned int i;
185 struct uasm_label labels[2];
186 struct uasm_reloc relocs[2];
187 struct uasm_label *l = labels;
188 struct uasm_reloc *r = relocs;
189
190 memset(labels, 0, sizeof(labels));
191 memset(relocs, 0, sizeof(relocs));
192
193 /* Set Guest EPC */
194 UASM_i_LW(&p, T0, offsetof(struct kvm_vcpu_arch, pc), K1);
195 uasm_i_mtc0(&p, T0, C0_EPC);
196
197 /* Set the ASID for the Guest Kernel */
198 UASM_i_LW(&p, T0, offsetof(struct kvm_vcpu_arch, cop0), K1);
199 UASM_i_LW(&p, T0, offsetof(struct mips_coproc, reg[MIPS_CP0_STATUS][0]),
200 T0);
201 uasm_i_andi(&p, T0, T0, KSU_USER | ST0_ERL | ST0_EXL);
202 uasm_i_xori(&p, T0, T0, KSU_USER);
203 uasm_il_bnez(&p, &r, T0, label_kernel_asid);
204 uasm_i_addiu(&p, T1, K1,
205 offsetof(struct kvm_vcpu_arch, guest_kernel_asid));
206 /* else user */
207 uasm_i_addiu(&p, T1, K1,
208 offsetof(struct kvm_vcpu_arch, guest_user_asid));
209 uasm_l_kernel_asid(&l, p);
210
211 /* t1: contains the base of the ASID array, need to get the cpu id */
212 /* smp_processor_id */
213 UASM_i_LW(&p, T2, offsetof(struct thread_info, cpu), GP);
214 /* x4 */
215 uasm_i_sll(&p, T2, T2, 2);
216 UASM_i_ADDU(&p, T3, T1, T2);
217 UASM_i_LW(&p, K0, 0, T3);
218#ifdef CONFIG_MIPS_ASID_BITS_VARIABLE
219 /* x sizeof(struct cpuinfo_mips)/4 */
220 uasm_i_addiu(&p, T3, ZERO, sizeof(struct cpuinfo_mips)/4);
221 uasm_i_mul(&p, T2, T2, T3);
222
223 UASM_i_LA_mostly(&p, AT, (long)&cpu_data[0].asid_mask);
224 UASM_i_ADDU(&p, AT, AT, T2);
225 UASM_i_LW(&p, T2, uasm_rel_lo((long)&cpu_data[0].asid_mask), AT);
226 uasm_i_and(&p, K0, K0, T2);
227#else
228 uasm_i_andi(&p, K0, K0, MIPS_ENTRYHI_ASID);
229#endif
230 uasm_i_mtc0(&p, K0, C0_ENTRYHI);
231 uasm_i_ehb(&p);
232
233 /* Disable RDHWR access */
234 uasm_i_mtc0(&p, ZERO, C0_HWRENA);
235
236 /* load the guest context from VCPU and return */
237 for (i = 1; i < 32; ++i) {
238 /* Guest k0/k1 loaded later */
239 if (i == K0 || i == K1)
240 continue;
241 UASM_i_LW(&p, i, offsetof(struct kvm_vcpu_arch, gprs[i]), K1);
242 }
243
244 /* Restore hi/lo */
245 UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, hi), K1);
246 uasm_i_mthi(&p, K0);
247
248 UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, lo), K1);
249 uasm_i_mtlo(&p, K0);
250
251 /* Restore the guest's k0/k1 registers */
252 UASM_i_LW(&p, K0, offsetof(struct kvm_vcpu_arch, gprs[K0]), K1);
253 UASM_i_LW(&p, K1, offsetof(struct kvm_vcpu_arch, gprs[K1]), K1);
254
255 /* Jump to guest */
256 uasm_i_eret(&p);
257
258 uasm_resolve_relocs(relocs, labels);
259
260 return p;
261}
262
263/**
264 * kvm_mips_build_exception() - Assemble first level guest exception handler.
265 * @addr: Address to start writing code.
266 *
267 * Assemble exception vector code for guest execution. The generated vector will
268 * jump to the common exception handler generated by kvm_mips_build_exit().
269 *
270 * Returns: Next address after end of written function.
271 */
272void *kvm_mips_build_exception(void *addr)
273{
274 u32 *p = addr;
275
276 /* Save guest k0 */
277 uasm_i_mtc0(&p, K0, C0_ERROREPC);
278 uasm_i_ehb(&p);
279
280 /* Get EBASE */
281 uasm_i_mfc0(&p, K0, C0_EBASE);
282 /* Get rid of CPUNum */
283 uasm_i_srl(&p, K0, K0, 10);
284 uasm_i_sll(&p, K0, K0, 10);
285 /* Save k1 @ offset 0x3000 */
286 UASM_i_SW(&p, K1, 0x3000, K0);
287
288 /* Exception handler is installed @ offset 0x2000 */
289 uasm_i_addiu(&p, K0, K0, 0x2000);
290 /* Jump to the function */
291 uasm_i_jr(&p, K0);
292 uasm_i_nop(&p);
293
294 return p;
295}
296
297/**
298 * kvm_mips_build_exit() - Assemble common guest exit handler.
299 * @addr: Address to start writing code.
300 *
301 * Assemble the generic guest exit handling code. This is called by the
302 * exception vectors (generated by kvm_mips_build_exception()), and calls
303 * kvm_mips_handle_exit(), then either resumes the guest or returns to the host
304 * depending on the return value.
305 *
306 * Returns: Next address after end of written function.
307 */
308void *kvm_mips_build_exit(void *addr)
309{
310 u32 *p = addr;
311 unsigned int i;
312 struct uasm_label labels[3];
313 struct uasm_reloc relocs[3];
314 struct uasm_label *l = labels;
315 struct uasm_reloc *r = relocs;
316
317 memset(labels, 0, sizeof(labels));
318 memset(relocs, 0, sizeof(relocs));
319
320 /*
321 * Generic Guest exception handler. We end up here when the guest
322 * does something that causes a trap to kernel mode.
323 */
324
325 /* Get the VCPU pointer from DDATA_LO */
326 uasm_i_mfc0(&p, K1, C0_DDATA_LO);
327 uasm_i_addiu(&p, K1, K1, offsetof(struct kvm_vcpu, arch));
328
329 /* Start saving Guest context to VCPU */
330 for (i = 0; i < 32; ++i) {
331 /* Guest k0/k1 saved later */
332 if (i == K0 || i == K1)
333 continue;
334 UASM_i_SW(&p, i, offsetof(struct kvm_vcpu_arch, gprs[i]), K1);
335 }
336
337 /* We need to save hi/lo and restore them on the way out */
338 uasm_i_mfhi(&p, T0);
339 UASM_i_SW(&p, T0, offsetof(struct kvm_vcpu_arch, hi), K1);
340
341 uasm_i_mflo(&p, T0);
342 UASM_i_SW(&p, T0, offsetof(struct kvm_vcpu_arch, lo), K1);
343
344 /* Finally save guest k0/k1 to VCPU */
345 uasm_i_mfc0(&p, T0, C0_ERROREPC);
346 UASM_i_SW(&p, T0, offsetof(struct kvm_vcpu_arch, gprs[K0]), K1);
347
348 /* Get GUEST k1 and save it in VCPU */
349 uasm_i_addiu(&p, T1, ZERO, ~0x2ff);
350 uasm_i_mfc0(&p, T0, C0_EBASE);
351 uasm_i_and(&p, T0, T0, T1);
352 UASM_i_LW(&p, T0, 0x3000, T0);
353 UASM_i_SW(&p, T0, offsetof(struct kvm_vcpu_arch, gprs[K1]), K1);
354
355 /* Now that context has been saved, we can use other registers */
356
357 /* Restore vcpu */
358 uasm_i_mfc0(&p, A1, C0_DDATA_LO);
359 uasm_i_move(&p, S1, A1);
360
361 /* Restore run (vcpu->run) */
362 UASM_i_LW(&p, A0, offsetof(struct kvm_vcpu, run), A1);
363 /* Save pointer to run in s0, will be saved by the compiler */
364 uasm_i_move(&p, S0, A0);
365
366 /*
367 * Save Host level EPC, BadVaddr and Cause to VCPU, useful to process
368 * the exception
369 */
370 uasm_i_mfc0(&p, K0, C0_EPC);
371 UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, pc), K1);
372
373 uasm_i_mfc0(&p, K0, C0_BADVADDR);
374 UASM_i_SW(&p, K0, offsetof(struct kvm_vcpu_arch, host_cp0_badvaddr),
375 K1);
376
377 uasm_i_mfc0(&p, K0, C0_CAUSE);
378 uasm_i_sw(&p, K0, offsetof(struct kvm_vcpu_arch, host_cp0_cause), K1);
379
380 /* Now restore the host state just enough to run the handlers */
381
382 /* Switch EBASE to the one used by Linux */
383 /* load up the host EBASE */
384 uasm_i_mfc0(&p, V0, C0_STATUS);
385
386 uasm_i_lui(&p, AT, ST0_BEV >> 16);
387 uasm_i_or(&p, K0, V0, AT);
388
389 uasm_i_mtc0(&p, K0, C0_STATUS);
390 uasm_i_ehb(&p);
391
392 UASM_i_LA_mostly(&p, K0, (long)&ebase);
393 UASM_i_LW(&p, K0, uasm_rel_lo((long)&ebase), K0);
394 uasm_i_mtc0(&p, K0, C0_EBASE);
395
396 /*
397 * If FPU is enabled, save FCR31 and clear it so that later ctc1's don't
398 * trigger FPE for pending exceptions.
399 */
400 uasm_i_lui(&p, AT, ST0_CU1 >> 16);
401 uasm_i_and(&p, V1, V0, AT);
402 uasm_il_beqz(&p, &r, V1, label_fpu_1);
403 uasm_i_nop(&p);
404 uasm_i_cfc1(&p, T0, 31);
405 uasm_i_sw(&p, T0, offsetof(struct kvm_vcpu_arch, fpu.fcr31), K1);
406 uasm_i_ctc1(&p, ZERO, 31);
407 uasm_l_fpu_1(&l, p);
408
409#ifdef CONFIG_CPU_HAS_MSA
410 /*
411 * If MSA is enabled, save MSACSR and clear it so that later
412 * instructions don't trigger MSAFPE for pending exceptions.
413 */
414 uasm_i_mfc0(&p, T0, C0_CONFIG3);
415 uasm_i_ext(&p, T0, T0, 28, 1); /* MIPS_CONF3_MSAP */
416 uasm_il_beqz(&p, &r, T0, label_msa_1);
417 uasm_i_nop(&p);
418 uasm_i_mfc0(&p, T0, C0_CONFIG5);
419 uasm_i_ext(&p, T0, T0, 27, 1); /* MIPS_CONF5_MSAEN */
420 uasm_il_beqz(&p, &r, T0, label_msa_1);
421 uasm_i_nop(&p);
422 uasm_i_cfcmsa(&p, T0, MSA_CSR);
423 uasm_i_sw(&p, T0, offsetof(struct kvm_vcpu_arch, fpu.msacsr),
424 K1);
425 uasm_i_ctcmsa(&p, MSA_CSR, ZERO);
426 uasm_l_msa_1(&l, p);
427#endif
428
429 /* Now that the new EBASE has been loaded, unset BEV and KSU_USER */
430 uasm_i_addiu(&p, AT, ZERO, ~(ST0_EXL | KSU_USER | ST0_IE));
431 uasm_i_and(&p, V0, V0, AT);
432 uasm_i_lui(&p, AT, ST0_CU0 >> 16);
433 uasm_i_or(&p, V0, V0, AT);
434 uasm_i_mtc0(&p, V0, C0_STATUS);
435 uasm_i_ehb(&p);
436
437 /* Load up host GP */
438 UASM_i_LW(&p, GP, offsetof(struct kvm_vcpu_arch, host_gp), K1);
439
440 /* Need a stack before we can jump to "C" */
441 UASM_i_LW(&p, SP, offsetof(struct kvm_vcpu_arch, host_stack), K1);
442
443 /* Saved host state */
444 uasm_i_addiu(&p, SP, SP, -(int)sizeof(struct pt_regs));
445
446 /*
447 * XXXKYMA do we need to load the host ASID, maybe not because the
448 * kernel entries are marked GLOBAL, need to verify
449 */
450
451 /* Restore host DDATA_LO */
452 UASM_i_LW(&p, K0, offsetof(struct pt_regs, cp0_epc), SP);
453 uasm_i_mtc0(&p, K0, C0_DDATA_LO);
454
455 /* Restore RDHWR access */
456 UASM_i_LA_mostly(&p, K0, (long)&hwrena);
457 uasm_i_lw(&p, K0, uasm_rel_lo((long)&hwrena), K0);
458 uasm_i_mtc0(&p, K0, C0_HWRENA);
459
460 /* Jump to handler */
461 /*
462 * XXXKYMA: not sure if this is safe, how large is the stack??
463 * Now jump to the kvm_mips_handle_exit() to see if we can deal
464 * with this in the kernel
465 */
466 UASM_i_LA(&p, T9, (unsigned long)kvm_mips_handle_exit);
467 uasm_i_jalr(&p, RA, T9);
468 uasm_i_addiu(&p, SP, SP, -CALLFRAME_SIZ);
469
470 uasm_resolve_relocs(relocs, labels);
471
472 p = kvm_mips_build_ret_from_exit(p);
473
474 return p;
475}
476
477/**
478 * kvm_mips_build_ret_from_exit() - Assemble guest exit return handler.
479 * @addr: Address to start writing code.
480 *
481 * Assemble the code to handle the return from kvm_mips_handle_exit(), either
482 * resuming the guest or returning to the host depending on the return value.
483 *
484 * Returns: Next address after end of written function.
485 */
486static void *kvm_mips_build_ret_from_exit(void *addr)
487{
488 u32 *p = addr;
489 struct uasm_label labels[2];
490 struct uasm_reloc relocs[2];
491 struct uasm_label *l = labels;
492 struct uasm_reloc *r = relocs;
493
494 memset(labels, 0, sizeof(labels));
495 memset(relocs, 0, sizeof(relocs));
496
497 /* Return from handler Make sure interrupts are disabled */
498 uasm_i_di(&p, ZERO);
499 uasm_i_ehb(&p);
500
501 /*
502 * XXXKYMA: k0/k1 could have been blown away if we processed
503 * an exception while we were handling the exception from the
504 * guest, reload k1
505 */
506
507 uasm_i_move(&p, K1, S1);
508 uasm_i_addiu(&p, K1, K1, offsetof(struct kvm_vcpu, arch));
509
510 /*
511 * Check return value, should tell us if we are returning to the
512 * host (handle I/O etc)or resuming the guest
513 */
514 uasm_i_andi(&p, T0, V0, RESUME_HOST);
515 uasm_il_bnez(&p, &r, T0, label_return_to_host);
516 uasm_i_nop(&p);
517
518 p = kvm_mips_build_ret_to_guest(p);
519
520 uasm_l_return_to_host(&l, p);
521 p = kvm_mips_build_ret_to_host(p);
522
523 uasm_resolve_relocs(relocs, labels);
524
525 return p;
526}
527
528/**
529 * kvm_mips_build_ret_to_guest() - Assemble code to return to the guest.
530 * @addr: Address to start writing code.
531 *
532 * Assemble the code to handle return from the guest exit handler
533 * (kvm_mips_handle_exit()) back to the guest.
534 *
535 * Returns: Next address after end of written function.
536 */
537static void *kvm_mips_build_ret_to_guest(void *addr)
538{
539 u32 *p = addr;
540
541 /* Put the saved pointer to vcpu (s1) back into the DDATA_LO Register */
542 uasm_i_mtc0(&p, S1, C0_DDATA_LO);
543
544 /* Load up the Guest EBASE to minimize the window where BEV is set */
545 UASM_i_LW(&p, T0, offsetof(struct kvm_vcpu_arch, guest_ebase), K1);
546
547 /* Switch EBASE back to the one used by KVM */
548 uasm_i_mfc0(&p, V1, C0_STATUS);
549 uasm_i_lui(&p, AT, ST0_BEV >> 16);
550 uasm_i_or(&p, K0, V1, AT);
551 uasm_i_mtc0(&p, K0, C0_STATUS);
552 uasm_i_ehb(&p);
553 uasm_i_mtc0(&p, T0, C0_EBASE);
554
555 /* Setup status register for running guest in UM */
556 uasm_i_ori(&p, V1, V1, ST0_EXL | KSU_USER | ST0_IE);
557 UASM_i_LA(&p, AT, ~(ST0_CU0 | ST0_MX));
558 uasm_i_and(&p, V1, V1, AT);
559 uasm_i_mtc0(&p, V1, C0_STATUS);
560 uasm_i_ehb(&p);
561
562 p = kvm_mips_build_enter_guest(p);
563
564 return p;
565}
566
567/**
568 * kvm_mips_build_ret_to_host() - Assemble code to return to the host.
569 * @addr: Address to start writing code.
570 *
571 * Assemble the code to handle return from the guest exit handler
572 * (kvm_mips_handle_exit()) back to the host, i.e. to the caller of the vcpu_run
573 * function generated by kvm_mips_build_vcpu_run().
574 *
575 * Returns: Next address after end of written function.
576 */
577static void *kvm_mips_build_ret_to_host(void *addr)
578{
579 u32 *p = addr;
580 unsigned int i;
581
582 /* EBASE is already pointing to Linux */
583 UASM_i_LW(&p, K1, offsetof(struct kvm_vcpu_arch, host_stack), K1);
584 uasm_i_addiu(&p, K1, K1, -(int)sizeof(struct pt_regs));
585
586 /* Restore host DDATA_LO */
587 UASM_i_LW(&p, K0, offsetof(struct pt_regs, cp0_epc), K1);
588 uasm_i_mtc0(&p, K0, C0_DDATA_LO);
589
590 /*
591 * r2/v0 is the return code, shift it down by 2 (arithmetic)
592 * to recover the err code
593 */
594 uasm_i_sra(&p, K0, V0, 2);
595 uasm_i_move(&p, V0, K0);
596
597 /* Load context saved on the host stack */
598 for (i = 16; i < 31; ++i) {
599 if (i == 24)
600 i = 28;
601 UASM_i_LW(&p, i, offsetof(struct pt_regs, regs[i]), K1);
602 }
603
604 UASM_i_LW(&p, K0, offsetof(struct pt_regs, hi), K1);
605 uasm_i_mthi(&p, K0);
606
607 UASM_i_LW(&p, K0, offsetof(struct pt_regs, lo), K1);
608 uasm_i_mtlo(&p, K0);
609
610 /* Restore RDHWR access */
611 UASM_i_LA_mostly(&p, K0, (long)&hwrena);
612 uasm_i_lw(&p, K0, uasm_rel_lo((long)&hwrena), K0);
613 uasm_i_mtc0(&p, K0, C0_HWRENA);
614
615 /* Restore RA, which is the address we will return to */
616 UASM_i_LW(&p, RA, offsetof(struct pt_regs, regs[RA]), K1);
617 uasm_i_jr(&p, RA);
618 uasm_i_nop(&p);
619
620 return p;
621}
622