dl_helper.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /* dl.c - arch-dependent part of loadable module support */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2013 Free Software Foundation, Inc.
  5. *
  6. * GRUB is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/dl.h>
  20. #include <grub/elf.h>
  21. #include <grub/misc.h>
  22. #include <grub/err.h>
  23. #include <grub/mm.h>
  24. #include <grub/i18n.h>
  25. #include <grub/arm/reloc.h>
  26. static inline grub_uint32_t
  27. thumb_get_instruction_word (grub_uint16_t *target)
  28. {
  29. /* Extract instruction word in alignment-safe manner */
  30. return grub_le_to_cpu16 ((*target)) << 16 | grub_le_to_cpu16 (*(target + 1));
  31. }
  32. static inline void
  33. thumb_set_instruction_word (grub_uint16_t *target, grub_uint32_t insword)
  34. {
  35. *target = grub_cpu_to_le16 (insword >> 16);
  36. *(target + 1) = grub_cpu_to_le16 (insword & 0xffff);
  37. }
  38. /*
  39. * R_ARM_ABS32
  40. *
  41. * Simple relocation of 32-bit value (in literal pool)
  42. */
  43. grub_err_t
  44. grub_arm_reloc_abs32 (Elf32_Word *target, Elf32_Addr sym_addr)
  45. {
  46. Elf32_Addr tmp;
  47. tmp = grub_le_to_cpu32 (*target);
  48. tmp += sym_addr;
  49. *target = grub_cpu_to_le32 (tmp);
  50. return GRUB_ERR_NONE;
  51. }
  52. /********************************************************************
  53. * Thumb (T32) relocations: *
  54. * *
  55. * 32-bit Thumb instructions can be 16-bit aligned, and are fetched *
  56. * little-endian, requiring some additional fiddling. *
  57. ********************************************************************/
  58. grub_int32_t
  59. grub_arm_thm_call_get_offset (grub_uint16_t *target)
  60. {
  61. grub_uint32_t sign, j1, j2;
  62. grub_uint32_t insword;
  63. grub_int32_t offset;
  64. insword = thumb_get_instruction_word (target);
  65. /* Extract bitfields from instruction words */
  66. sign = (insword >> 26) & 1;
  67. j1 = (insword >> 13) & 1;
  68. j2 = (insword >> 11) & 1;
  69. offset = (sign << 24) | ((~(j1 ^ sign) & 1) << 23) |
  70. ((~(j2 ^ sign) & 1) << 22) |
  71. ((insword & 0x03ff0000) >> 4) | ((insword & 0x000007ff) << 1);
  72. /* Sign adjust and calculate offset */
  73. if (offset & (1 << 24))
  74. offset -= (1 << 25);
  75. return offset;
  76. }
  77. grub_err_t
  78. grub_arm_thm_call_set_offset (grub_uint16_t *target, grub_int32_t offset)
  79. {
  80. grub_uint32_t sign, j1, j2;
  81. const grub_uint32_t insmask = 0xf800d000;
  82. grub_uint32_t insword;
  83. int is_blx;
  84. insword = thumb_get_instruction_word (target);
  85. if (((insword >> 12) & 0xd) == 0xc)
  86. is_blx = 1;
  87. else
  88. is_blx = 0;
  89. if (!is_blx && !(offset & 1))
  90. return grub_error (GRUB_ERR_BAD_MODULE, "bl/b.w targettting ARM");
  91. /* Transform blx into bl if necessarry. */
  92. if (is_blx && (offset & 1))
  93. insword |= (1 << 12);
  94. /* Reassemble instruction word */
  95. sign = (offset >> 24) & 1;
  96. j1 = sign ^ (~(offset >> 23) & 1);
  97. j2 = sign ^ (~(offset >> 22) & 1);
  98. insword = (insword & insmask) |
  99. (sign << 26) |
  100. (((offset >> 12) & 0x03ff) << 16) |
  101. (j1 << 13) | (j2 << 11) | ((offset >> 1) & 0x07ff);
  102. thumb_set_instruction_word (target, insword);
  103. grub_dprintf ("dl", " *insword = 0x%08x", insword);
  104. return GRUB_ERR_NONE;
  105. }
  106. grub_int32_t
  107. grub_arm_thm_jump19_get_offset (grub_uint16_t *target)
  108. {
  109. grub_int32_t offset;
  110. grub_uint32_t insword;
  111. insword = thumb_get_instruction_word (target);
  112. /* Extract and sign extend offset */
  113. offset = ((insword >> 26) & 1) << 19
  114. | ((insword >> 11) & 1) << 18
  115. | ((insword >> 13) & 1) << 17
  116. | ((insword >> 16) & 0x3f) << 11
  117. | (insword & 0x7ff);
  118. offset <<= 1;
  119. if (offset & (1 << 20))
  120. offset -= (1 << 21);
  121. return offset;
  122. }
  123. void
  124. grub_arm_thm_jump19_set_offset (grub_uint16_t *target, grub_int32_t offset)
  125. {
  126. grub_uint32_t insword;
  127. const grub_uint32_t insmask = 0xfbc0d000;
  128. offset >>= 1;
  129. offset &= 0xfffff;
  130. insword = thumb_get_instruction_word (target);
  131. /* Reassemble instruction word and write back */
  132. insword &= insmask;
  133. insword |= ((offset >> 19) & 1) << 26
  134. | ((offset >> 18) & 1) << 11
  135. | ((offset >> 17) & 1) << 13
  136. | ((offset >> 11) & 0x3f) << 16
  137. | (offset & 0x7ff);
  138. thumb_set_instruction_word (target, insword);
  139. }
  140. int
  141. grub_arm_thm_jump19_check_offset (grub_int32_t offset)
  142. {
  143. if ((offset > 1048574) || (offset < -1048576))
  144. return 0;
  145. return 1;
  146. }
  147. grub_uint16_t
  148. grub_arm_thm_movw_movt_get_value (grub_uint16_t *target)
  149. {
  150. grub_uint32_t insword;
  151. insword = thumb_get_instruction_word (target);
  152. return ((insword & 0xf0000) >> 4) | ((insword & 0x04000000) >> 15) | \
  153. ((insword & 0x7000) >> 4) | (insword & 0xff);
  154. }
  155. void
  156. grub_arm_thm_movw_movt_set_value (grub_uint16_t *target, grub_uint16_t value)
  157. {
  158. grub_uint32_t insword;
  159. const grub_uint32_t insmask = 0xfbf08f00;
  160. insword = thumb_get_instruction_word (target);
  161. insword &= insmask;
  162. insword |= ((value & 0xf000) << 4) | ((value & 0x0800) << 15) | \
  163. ((value & 0x0700) << 4) | (value & 0xff);
  164. thumb_set_instruction_word (target, insword);
  165. }
  166. /***********************************************************
  167. * ARM (A32) relocations: *
  168. * *
  169. * ARM instructions are 32-bit in size and 32-bit aligned. *
  170. ***********************************************************/
  171. grub_int32_t
  172. grub_arm_jump24_get_offset (grub_uint32_t *target)
  173. {
  174. grub_int32_t offset;
  175. grub_uint32_t insword;
  176. insword = grub_le_to_cpu32 (*target);
  177. offset = (insword & 0x00ffffff) << 2;
  178. if (offset & 0x02000000)
  179. offset -= 0x04000000;
  180. return offset;
  181. }
  182. int
  183. grub_arm_jump24_check_offset (grub_int32_t offset)
  184. {
  185. if (offset >= 0x02000000 || offset < -0x02000000)
  186. return 0;
  187. return 1;
  188. }
  189. void
  190. grub_arm_jump24_set_offset (grub_uint32_t *target,
  191. grub_int32_t offset)
  192. {
  193. grub_uint32_t insword;
  194. insword = grub_le_to_cpu32 (*target);
  195. insword &= 0xff000000;
  196. insword |= (offset >> 2) & 0x00ffffff;
  197. *target = grub_cpu_to_le32 (insword);
  198. }