cache.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #include <grub/dl.h>
  2. #include <grub/cache.h>
  3. #include <grub/arm/system.h>
  4. #ifdef GRUB_MACHINE_UBOOT
  5. #include <grub/uboot/uboot.h>
  6. #include <grub/uboot/api_public.h>
  7. #include <grub/mm.h>
  8. #endif
  9. /* This is only about cache architecture. It doesn't imply
  10. the CPU architecture. */
  11. static enum
  12. {
  13. ARCH_UNKNOWN,
  14. ARCH_ARMV5_WRITE_THROUGH,
  15. ARCH_ARMV6,
  16. ARCH_ARMV6_UNIFIED,
  17. ARCH_ARMV7
  18. } type = ARCH_UNKNOWN;
  19. static int is_v6_mmu;
  20. static grub_uint32_t grub_arch_cache_dlinesz;
  21. static grub_uint32_t grub_arch_cache_ilinesz;
  22. static grub_uint32_t grub_arch_cache_max_linesz;
  23. /* Prototypes for asm functions. */
  24. void grub_arm_clean_dcache_range_armv6 (grub_addr_t start, grub_addr_t end,
  25. grub_addr_t dlinesz);
  26. void grub_arm_clean_dcache_range_armv7 (grub_addr_t start, grub_addr_t end,
  27. grub_addr_t dlinesz);
  28. void grub_arm_clean_dcache_range_poc_armv7 (grub_addr_t start, grub_addr_t end,
  29. grub_addr_t dlinesz);
  30. void grub_arm_invalidate_icache_range_armv6 (grub_addr_t start, grub_addr_t end,
  31. grub_addr_t dlinesz);
  32. void grub_arm_invalidate_icache_range_armv7 (grub_addr_t start, grub_addr_t end,
  33. grub_addr_t dlinesz);
  34. void grub_arm_disable_caches_mmu_armv6 (void);
  35. void grub_arm_disable_caches_mmu_armv7 (void);
  36. grub_uint32_t grub_arm_main_id (void);
  37. grub_uint32_t grub_arm_cache_type (void);
  38. static void
  39. probe_caches (void)
  40. {
  41. grub_uint32_t main_id, cache_type;
  42. /* Read main ID Register */
  43. main_id = grub_arm_main_id ();
  44. switch ((main_id >> 16) & 0xf)
  45. {
  46. case 0x3:
  47. case 0x4:
  48. case 0x5:
  49. case 0x6:
  50. is_v6_mmu = 0;
  51. break;
  52. case 0x7:
  53. case 0xf:
  54. is_v6_mmu = 1;
  55. break;
  56. default:
  57. grub_fatal ("Unsupported ARM ID 0x%x", main_id);
  58. }
  59. /* Read Cache Type Register */
  60. cache_type = grub_arm_cache_type ();
  61. switch (cache_type >> 24)
  62. {
  63. case 0x00:
  64. case 0x01:
  65. grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3);
  66. grub_arch_cache_ilinesz = 8 << (cache_type & 3);
  67. type = ARCH_ARMV5_WRITE_THROUGH;
  68. break;
  69. case 0x04:
  70. case 0x0a:
  71. case 0x0c:
  72. case 0x0e:
  73. case 0x1c:
  74. grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3);
  75. grub_arch_cache_ilinesz = 8 << (cache_type & 3);
  76. type = ARCH_ARMV6_UNIFIED;
  77. break;
  78. case 0x05:
  79. case 0x0b:
  80. case 0x0d:
  81. case 0x0f:
  82. case 0x1d:
  83. grub_arch_cache_dlinesz = 8 << ((cache_type >> 12) & 3);
  84. grub_arch_cache_ilinesz = 8 << (cache_type & 3);
  85. type = ARCH_ARMV6;
  86. break;
  87. default:
  88. /*
  89. * The CTR register is pretty much unchanged from v7 onwards,
  90. * and is guaranteed to be backward compatible (the IDC/DIC bits
  91. * allow certain CMOs to be elided, but performing them is never
  92. * wrong), hence handling it like its AArch64 equivalent.
  93. */
  94. grub_arch_cache_dlinesz = 4 << ((cache_type >> 16) & 0xf);
  95. grub_arch_cache_ilinesz = 4 << (cache_type & 0xf);
  96. type = ARCH_ARMV7;
  97. }
  98. if (grub_arch_cache_dlinesz > grub_arch_cache_ilinesz)
  99. grub_arch_cache_max_linesz = grub_arch_cache_dlinesz;
  100. else
  101. grub_arch_cache_max_linesz = grub_arch_cache_ilinesz;
  102. }
  103. #ifdef GRUB_MACHINE_UBOOT
  104. static void subdivide (grub_uint32_t *table, grub_uint32_t *subtable,
  105. grub_uint32_t addr)
  106. {
  107. grub_uint32_t j;
  108. addr = addr >> 20 << 20;
  109. table[addr >> 20] = (grub_addr_t) subtable | 1;
  110. for (j = 0; j < 256; j++)
  111. subtable[j] = addr | (j << 12)
  112. | (3 << 4) | (3 << 6) | (3 << 8) | (3 << 10)
  113. | (0 << 3) | (1 << 2) | 2;
  114. }
  115. void
  116. grub_arm_enable_caches_mmu (void)
  117. {
  118. grub_uint32_t *table;
  119. grub_uint32_t i;
  120. grub_uint32_t border_crossing = 0;
  121. grub_uint32_t *subtable;
  122. struct sys_info *si = grub_uboot_get_sys_info ();
  123. if (!si || (si->mr_no == 0))
  124. {
  125. grub_printf ("couldn't get memory map, not enabling caches");
  126. grub_errno = GRUB_ERR_NONE;
  127. return;
  128. }
  129. if (type == ARCH_UNKNOWN)
  130. probe_caches ();
  131. for (i = 0; (signed) i < si->mr_no; i++)
  132. {
  133. if (si->mr[i].start & ((1 << 20) - 1))
  134. border_crossing++;
  135. if ((si->mr[i].start + si->mr[i].size) & ((1 << 20) - 1))
  136. border_crossing++;
  137. }
  138. grub_printf ("%d crossers\n", border_crossing);
  139. table = grub_memalign (1 << 14, (1 << 14) + (border_crossing << 10));
  140. if (!table)
  141. {
  142. grub_printf ("couldn't allocate place for MMU table, not enabling caches");
  143. grub_errno = GRUB_ERR_NONE;
  144. return;
  145. }
  146. subtable = table + (1 << 12);
  147. /* Map all unknown as device. */
  148. for (i = 0; i < (1 << 12); i++)
  149. table[i] = (i << 20) | (3 << 10) | (0 << 3) | (1 << 2) | 2;
  150. /*
  151. Device: TEX= 0, C=0, B=1
  152. normal: TEX= 0, C=1, B=1
  153. AP = 3
  154. IMP = 0
  155. Domain = 0
  156. */
  157. for (i = 0; (signed) i < si->mr_no; i++)
  158. {
  159. if (si->mr[i].start & ((1 << 20) - 1))
  160. {
  161. subdivide (table, subtable, si->mr[i].start);
  162. subtable += (1 << 8);
  163. }
  164. if ((si->mr[i].start + si->mr[i].size) & ((1 << 20) - 1))
  165. {
  166. subdivide (table, subtable, si->mr[i].start + si->mr[i].size);
  167. subtable += (1 << 8);
  168. }
  169. }
  170. for (i = 0; (signed) i < si->mr_no; i++)
  171. if ((si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_DRAM
  172. || (si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_SRAM
  173. || (si->mr[i].flags & MR_ATTR_MASK) == MR_ATTR_FLASH)
  174. {
  175. grub_uint32_t cur, end;
  176. cur = si->mr[i].start;
  177. end = si->mr[i].start + si->mr[i].size;
  178. while (cur < end)
  179. {
  180. grub_uint32_t *st;
  181. if ((table[cur >> 20] & 3) == 2)
  182. {
  183. cur = cur >> 20 << 20;
  184. table[cur >> 20] = cur | (3 << 10) | (1 << 3) | (1 << 2) | 2;
  185. cur += (1 << 20);
  186. continue;
  187. }
  188. cur = cur >> 12 << 12;
  189. st = (grub_uint32_t *) (table[cur >> 20] & ~0x3ff);
  190. st[(cur >> 12) & 0xff] = cur | (3 << 4) | (3 << 6)
  191. | (3 << 8) | (3 << 10)
  192. | (1 << 3) | (1 << 2) | 2;
  193. cur += (1 << 12);
  194. }
  195. }
  196. grub_printf ("MMU tables generated\n");
  197. if (is_v6_mmu)
  198. grub_arm_clear_mmu_v6 ();
  199. grub_printf ("enabling MMU\n");
  200. grub_arm_enable_mmu (table);
  201. grub_printf ("MMU enabled\n");
  202. }
  203. #endif
  204. void
  205. grub_arch_sync_caches (void *address, grub_size_t len)
  206. {
  207. grub_addr_t start = (grub_addr_t) address;
  208. grub_addr_t end = start + len;
  209. if (type == ARCH_UNKNOWN)
  210. probe_caches ();
  211. start = ALIGN_DOWN (start, grub_arch_cache_max_linesz);
  212. end = ALIGN_UP (end, grub_arch_cache_max_linesz);
  213. switch (type)
  214. {
  215. case ARCH_ARMV6:
  216. grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz);
  217. grub_arm_invalidate_icache_range_armv6 (start, end,
  218. grub_arch_cache_ilinesz);
  219. break;
  220. case ARCH_ARMV7:
  221. grub_arm_clean_dcache_range_armv7 (start, end, grub_arch_cache_dlinesz);
  222. grub_arm_invalidate_icache_range_armv7 (start, end,
  223. grub_arch_cache_ilinesz);
  224. break;
  225. /* Nothing to do. */
  226. case ARCH_ARMV5_WRITE_THROUGH:
  227. case ARCH_ARMV6_UNIFIED:
  228. break;
  229. /* Pacify GCC. */
  230. case ARCH_UNKNOWN:
  231. break;
  232. }
  233. }
  234. void
  235. grub_arch_sync_dma_caches (volatile void *address, grub_size_t len)
  236. {
  237. grub_addr_t start = (grub_addr_t) address;
  238. grub_addr_t end = start + len;
  239. if (type == ARCH_UNKNOWN)
  240. probe_caches ();
  241. start = ALIGN_DOWN (start, grub_arch_cache_max_linesz);
  242. end = ALIGN_UP (end, grub_arch_cache_max_linesz);
  243. switch (type)
  244. {
  245. case ARCH_ARMV6:
  246. grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz);
  247. grub_arm_invalidate_icache_range_armv6 (start, end,
  248. grub_arch_cache_ilinesz);
  249. break;
  250. case ARCH_ARMV5_WRITE_THROUGH:
  251. case ARCH_ARMV6_UNIFIED:
  252. grub_arm_clean_dcache_range_armv6 (start, end, grub_arch_cache_dlinesz);
  253. break;
  254. case ARCH_ARMV7:
  255. grub_arm_clean_dcache_range_poc_armv7 (start, end, grub_arch_cache_dlinesz);
  256. grub_arm_invalidate_icache_range_armv7 (start, end,
  257. grub_arch_cache_ilinesz);
  258. break;
  259. /* Pacify GCC. */
  260. case ARCH_UNKNOWN:
  261. break;
  262. }
  263. }
  264. void
  265. grub_arm_disable_caches_mmu (void)
  266. {
  267. if (type == ARCH_UNKNOWN)
  268. probe_caches ();
  269. switch (type)
  270. {
  271. case ARCH_ARMV5_WRITE_THROUGH:
  272. case ARCH_ARMV6_UNIFIED:
  273. case ARCH_ARMV6:
  274. grub_arm_disable_caches_mmu_armv6 ();
  275. break;
  276. case ARCH_ARMV7:
  277. grub_arm_disable_caches_mmu_armv7 ();
  278. break;
  279. /* Pacify GCC. */
  280. case ARCH_UNKNOWN:
  281. break;
  282. }
  283. }