dl_helper.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2023 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/dl.h>
  19. #include <grub/elf.h>
  20. #include <grub/misc.h>
  21. #include <grub/err.h>
  22. #include <grub/mm.h>
  23. #include <grub/i18n.h>
  24. #include <grub/loongarch64/reloc.h>
  25. /*
  26. * LoongArch relocations documentation:
  27. * https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#relocations
  28. */
  29. static void grub_loongarch64_stack_push (grub_loongarch64_stack_t stack, grub_uint64_t x);
  30. static grub_uint64_t grub_loongarch64_stack_pop (grub_loongarch64_stack_t stack);
  31. void
  32. grub_loongarch64_stack_init (grub_loongarch64_stack_t stack)
  33. {
  34. stack->top = -1;
  35. stack->count = LOONGARCH64_STACK_MAX;
  36. }
  37. static void
  38. grub_loongarch64_stack_push (grub_loongarch64_stack_t stack, grub_uint64_t x)
  39. {
  40. if (stack->top == stack->count)
  41. return;
  42. stack->data[++stack->top] = x;
  43. }
  44. static grub_uint64_t
  45. grub_loongarch64_stack_pop (grub_loongarch64_stack_t stack)
  46. {
  47. if (stack->top == -1)
  48. return 0;
  49. return stack->data[stack->top--];
  50. }
  51. void
  52. grub_loongarch64_sop_push (grub_loongarch64_stack_t stack, grub_int64_t offset)
  53. {
  54. grub_loongarch64_stack_push (stack, offset);
  55. }
  56. /* opr2 = pop (), opr1 = pop (), push (opr1 - opr2) */
  57. void
  58. grub_loongarch64_sop_sub (grub_loongarch64_stack_t stack)
  59. {
  60. grub_uint64_t a, b;
  61. b = grub_loongarch64_stack_pop (stack);
  62. a = grub_loongarch64_stack_pop (stack);
  63. grub_loongarch64_stack_push (stack, a - b);
  64. }
  65. /* opr2 = pop (), opr1 = pop (), push (opr1 << opr2) */
  66. void
  67. grub_loongarch64_sop_sl (grub_loongarch64_stack_t stack)
  68. {
  69. grub_uint64_t a, b;
  70. b = grub_loongarch64_stack_pop (stack);
  71. a = grub_loongarch64_stack_pop (stack);
  72. grub_loongarch64_stack_push (stack, a << b);
  73. }
  74. /* opr2 = pop (), opr1 = pop (), push (opr1 >> opr2) */
  75. void
  76. grub_loongarch64_sop_sr (grub_loongarch64_stack_t stack)
  77. {
  78. grub_uint64_t a, b;
  79. b = grub_loongarch64_stack_pop (stack);
  80. a = grub_loongarch64_stack_pop (stack);
  81. grub_loongarch64_stack_push (stack, a >> b);
  82. }
  83. /* opr2 = pop (), opr1 = pop (), push (opr1 + opr2) */
  84. void
  85. grub_loongarch64_sop_add (grub_loongarch64_stack_t stack)
  86. {
  87. grub_uint64_t a, b;
  88. b = grub_loongarch64_stack_pop (stack);
  89. a = grub_loongarch64_stack_pop (stack);
  90. grub_loongarch64_stack_push (stack, a + b);
  91. }
  92. /* opr2 = pop (), opr1 = pop (), push (opr1 & opr2) */
  93. void
  94. grub_loongarch64_sop_and (grub_loongarch64_stack_t stack)
  95. {
  96. grub_uint64_t a, b;
  97. b = grub_loongarch64_stack_pop (stack);
  98. a = grub_loongarch64_stack_pop (stack);
  99. grub_loongarch64_stack_push (stack, a & b);
  100. }
  101. /* opr3 = pop (), opr2 = pop (), opr1 = pop (), push (opr1 ? opr2 : opr3) */
  102. void
  103. grub_loongarch64_sop_if_else (grub_loongarch64_stack_t stack)
  104. {
  105. grub_uint64_t a, b, c;
  106. c = grub_loongarch64_stack_pop (stack);
  107. b = grub_loongarch64_stack_pop (stack);
  108. a = grub_loongarch64_stack_pop (stack);
  109. if (a) {
  110. grub_loongarch64_stack_push (stack, b);
  111. } else {
  112. grub_loongarch64_stack_push (stack, c);
  113. }
  114. }
  115. /* opr1 = pop (), (*(uint32_t *) PC) [14 ... 10] = opr1 [4 ... 0] */
  116. void
  117. grub_loongarch64_sop_32_s_10_5 (grub_loongarch64_stack_t stack,
  118. grub_uint64_t *place)
  119. {
  120. grub_uint64_t a = grub_loongarch64_stack_pop (stack);
  121. *place |= ((a & 0x1f) << 10);
  122. }
  123. /* opr1 = pop (), (*(uint32_t *) PC) [21 ... 10] = opr1 [11 ... 0] */
  124. void
  125. grub_loongarch64_sop_32_u_10_12 (grub_loongarch64_stack_t stack,
  126. grub_uint64_t *place)
  127. {
  128. grub_uint64_t a = grub_loongarch64_stack_pop (stack);
  129. *place = *place | ((a & 0xfff) << 10);
  130. }
  131. /* opr1 = pop (), (*(uint32_t *) PC) [21 ... 10] = opr1 [11 ... 0] */
  132. void
  133. grub_loongarch64_sop_32_s_10_12 (grub_loongarch64_stack_t stack,
  134. grub_uint64_t *place)
  135. {
  136. grub_uint64_t a = grub_loongarch64_stack_pop (stack);
  137. *place = (*place) | ((a & 0xfff) << 10);
  138. }
  139. /* opr1 = pop (), (*(uint32_t *) PC) [25 ... 10] = opr1 [15 ... 0] */
  140. void
  141. grub_loongarch64_sop_32_s_10_16 (grub_loongarch64_stack_t stack,
  142. grub_uint64_t *place)
  143. {
  144. grub_uint64_t a = grub_loongarch64_stack_pop (stack);
  145. *place = (*place) | ((a & 0xffff) << 10);
  146. }
  147. /* opr1 = pop (), (*(uint32_t *) PC) [25 ... 10] = opr1 [17 ... 2] */
  148. void
  149. grub_loongarch64_sop_32_s_10_16_s2 (grub_loongarch64_stack_t stack,
  150. grub_uint64_t *place)
  151. {
  152. grub_uint64_t a = grub_loongarch64_stack_pop (stack);
  153. *place = (*place) | (((a >> 2) & 0xffff) << 10);
  154. }
  155. /* opr1 = pop (), (*(uint32_t *) PC) [24 ... 5] = opr1 [19 ... 0] */
  156. void
  157. grub_loongarch64_sop_32_s_5_20 (grub_loongarch64_stack_t stack, grub_uint64_t *place)
  158. {
  159. grub_uint64_t a = grub_loongarch64_stack_pop (stack);
  160. *place = (*place) | ((a & 0xfffff)<<5);
  161. }
  162. /* opr1 = pop (), (*(uint32_t *) PC) [4 ... 0] = opr1 [22 ... 18] */
  163. void
  164. grub_loongarch64_sop_32_s_0_5_10_16_s2 (grub_loongarch64_stack_t stack,
  165. grub_uint64_t *place)
  166. {
  167. grub_uint64_t a = grub_loongarch64_stack_pop (stack);
  168. *place =(*place) | (((a >> 2) & 0xffff) << 10);
  169. *place =(*place) | ((a >> 18) & 0x1f);
  170. }
  171. /*
  172. * opr1 = pop ()
  173. * (*(uint32_t *) PC) [9 ... 0] = opr1 [27 ... 18],
  174. * (*(uint32_t *) PC) [25 ... 10] = opr1 [17 ... 2]
  175. */
  176. void
  177. grub_loongarch64_sop_32_s_0_10_10_16_s2 (grub_loongarch64_stack_t stack,
  178. grub_uint64_t *place)
  179. {
  180. grub_uint64_t a = grub_loongarch64_stack_pop (stack);
  181. *place =(*place) | (((a >> 2) & 0xffff) << 10);
  182. *place =(*place) | ((a >> 18) & 0x3ff);
  183. }
  184. /*
  185. * B26 relocation for the 28-bit PC-relative jump
  186. * (*(uint32_t *) PC) [9 ... 0] = (S + A - PC) [27 ... 18]
  187. * (*(uint32_t *) PC) [25 ... 10] = (S + A - PC) [17 ... 2]
  188. */
  189. void grub_loongarch64_b26 (grub_uint32_t *place, grub_int64_t offset)
  190. {
  191. grub_uint32_t val;
  192. const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfc000000);
  193. grub_dprintf ("dl", " reloc_b26 %p %c= 0x%" PRIxGRUB_INT64_T "\n",
  194. place, offset > 0 ? '+' : '-',
  195. offset < 0 ? -offset : offset);
  196. val = ((offset >> 18) & 0x3ff) | (((offset >> 2) & 0xffff) << 10);
  197. *place &= insmask;
  198. *place |= grub_cpu_to_le32 (val) & ~insmask;
  199. }
  200. /*
  201. * ABS_HI20/PCALA_HI20 relocations for 32/64-bit absolute address/PC-relative offset
  202. * (*(uint32_t *) PC) [24 ... 5] = (S + A) [31 ... 12]
  203. */
  204. void grub_loongarch64_xxx_hi20 (grub_uint32_t *place, grub_int64_t offset)
  205. {
  206. const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfe00001f);
  207. grub_uint32_t val;
  208. offset >>= 12;
  209. val = ((offset & 0xfffff) << 5);
  210. *place &= insmask;
  211. *place |= grub_cpu_to_le32 (val) & ~insmask;
  212. }
  213. /*
  214. * ABS_LO12/PCALA_LO12 relocations for 32/64-bit absolute address
  215. * (*(uint32_t *) PC) [21 ... 10] = (S + A) [11 ... 0]
  216. */
  217. void grub_loongarch64_xxx_lo12 (grub_uint32_t *place, grub_int64_t offset)
  218. {
  219. const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xffc003ff);
  220. *place &= insmask;
  221. *place |= grub_cpu_to_le32 (offset << 10) & ~insmask;
  222. }
  223. /*
  224. * ABS64_HI12 relocation for the 64-bit absolute address
  225. * (*(uint32_t *) PC) [21 ... 10] = (S + A) [63 ... 52]
  226. */
  227. void grub_loongarch64_abs64_hi12 (grub_uint32_t *place, grub_int64_t offset)
  228. {
  229. const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xffc003ff);
  230. grub_uint32_t val;
  231. offset >>= 52;
  232. val = ((offset & 0xfff) << 10);
  233. *place &= insmask;
  234. *place |= grub_cpu_to_le32 (val) & ~insmask;
  235. }
  236. /*
  237. * ABS64_LO20 relocation for the 64-bit absolute address
  238. * (*(uint32_t *) PC) [24 ... 5] = (S + A) [51 ... 32]
  239. */
  240. void grub_loongarch64_abs64_lo20 (grub_uint32_t *place, grub_int64_t offset)
  241. {
  242. const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfe00001f);
  243. grub_uint32_t val;
  244. offset >>= 32;
  245. val = ((offset & 0xfffff) << 5);
  246. *place &= insmask;
  247. *place |= grub_cpu_to_le32 (val) & ~insmask;
  248. }