relocate_kernel.S 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. /*
  3. * relocate_kernel.S for kexec
  4. *
  5. * Copyright (C) 2022 Loongson Technology Corporation Limited
  6. */
  7. #include <linux/kexec.h>
  8. #include <asm/asm.h>
  9. #include <asm/asmmacro.h>
  10. #include <asm/regdef.h>
  11. #include <asm/loongarch.h>
  12. #include <asm/stackframe.h>
  13. #include <asm/addrspace.h>
  14. SYM_CODE_START(relocate_new_kernel)
  15. /*
  16. * a0: EFI boot flag for the new kernel
  17. * a1: Command line pointer for the new kernel
  18. * a2: System table pointer for the new kernel
  19. * a3: Start address to jump to after relocation
  20. * a4: Pointer to the current indirection page entry
  21. */
  22. move s0, a4
  23. /*
  24. * In case of a kdump/crash kernel, the indirection page is not
  25. * populated as the kernel is directly copied to a reserved location
  26. */
  27. beqz s0, done
  28. process_entry:
  29. PTR_L s1, s0, 0
  30. PTR_ADDI s0, s0, SZREG
  31. /* destination page */
  32. andi s2, s1, IND_DESTINATION
  33. beqz s2, 1f
  34. li.w t0, ~0x1
  35. and s3, s1, t0 /* store destination addr in s3 */
  36. b process_entry
  37. 1:
  38. /* indirection page, update s0 */
  39. andi s2, s1, IND_INDIRECTION
  40. beqz s2, 1f
  41. li.w t0, ~0x2
  42. and s0, s1, t0
  43. b process_entry
  44. 1:
  45. /* done page */
  46. andi s2, s1, IND_DONE
  47. beqz s2, 1f
  48. b done
  49. 1:
  50. /* source page */
  51. andi s2, s1, IND_SOURCE
  52. beqz s2, process_entry
  53. li.w t0, ~0x8
  54. and s1, s1, t0
  55. li.w s5, (1 << _PAGE_SHIFT) / SZREG
  56. copy_word:
  57. /* copy page word by word */
  58. REG_L s4, s1, 0
  59. REG_S s4, s3, 0
  60. PTR_ADDI s3, s3, SZREG
  61. PTR_ADDI s1, s1, SZREG
  62. LONG_ADDI s5, s5, -1
  63. beqz s5, process_entry
  64. b copy_word
  65. b process_entry
  66. done:
  67. ibar 0
  68. dbar 0
  69. /*
  70. * Jump to the new kernel,
  71. * make sure the values of a0, a1, a2 and a3 are not changed.
  72. */
  73. jr a3
  74. SYM_CODE_END(relocate_new_kernel)
  75. #ifdef CONFIG_SMP
  76. /*
  77. * Other CPUs should wait until code is relocated and
  78. * then start at the entry point from LOONGARCH_IOCSR_MBUF0.
  79. */
  80. SYM_CODE_START(kexec_smp_wait)
  81. 1: li.w t0, 0x100 /* wait for init loop */
  82. 2: addi.w t0, t0, -1 /* limit mailbox access */
  83. bnez t0, 2b
  84. li.w t1, LOONGARCH_IOCSR_MBUF0
  85. iocsrrd.w s0, t1 /* check PC as an indicator */
  86. beqz s0, 1b
  87. iocsrrd.d s0, t1 /* get PC via mailbox */
  88. li.d t0, CACHE_BASE
  89. or s0, s0, t0 /* s0 = TO_CACHE(s0) */
  90. jr s0 /* jump to initial PC */
  91. SYM_CODE_END(kexec_smp_wait)
  92. #endif
  93. relocate_new_kernel_end:
  94. SYM_DATA_START(relocate_new_kernel_size)
  95. PTR relocate_new_kernel_end - relocate_new_kernel
  96. SYM_DATA_END(relocate_new_kernel_size)