fdt.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2016 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/fdtbus.h>
  19. #include <grub/fdt.h>
  20. #include <grub/term.h>
  21. static const void *dtb;
  22. static grub_size_t root_address_cells, root_size_cells;
  23. /* Pointer to this symbol signals invalid mapping. */
  24. char grub_fdtbus_invalid_mapping[1];
  25. struct grub_fdtbus_dev
  26. {
  27. struct grub_fdtbus_dev *next;
  28. struct grub_fdtbus_dev *parent;
  29. int node;
  30. struct grub_fdtbus_driver *driver;
  31. };
  32. struct grub_fdtbus_dev *devs;
  33. struct grub_fdtbus_driver *drivers;
  34. static int
  35. is_compatible (struct grub_fdtbus_driver *driver,
  36. int node)
  37. {
  38. grub_size_t compatible_size;
  39. const char *compatible = grub_fdt_get_prop (dtb, node, "compatible",
  40. &compatible_size);
  41. const char *compatible_end = compatible + compatible_size;
  42. while (compatible < compatible_end)
  43. {
  44. if (grub_strcmp (driver->compatible, compatible) == 0)
  45. return 1;
  46. compatible += grub_strlen (compatible) + 1;
  47. }
  48. return 0;
  49. }
  50. static void
  51. fdtbus_scan (struct grub_fdtbus_dev *parent)
  52. {
  53. int node;
  54. for (node = grub_fdt_first_node (dtb, parent ? parent->node : 0); node >= 0;
  55. node = grub_fdt_next_node (dtb, node))
  56. {
  57. struct grub_fdtbus_dev *dev;
  58. struct grub_fdtbus_driver *driver;
  59. dev = grub_zalloc (sizeof (*dev));
  60. if (!dev)
  61. {
  62. grub_print_error ();
  63. return;
  64. }
  65. dev->node = node;
  66. dev->next = devs;
  67. dev->parent = parent;
  68. devs = dev;
  69. FOR_LIST_ELEMENTS(driver, drivers)
  70. if (!dev->driver && is_compatible (driver, node))
  71. {
  72. if (driver->attach(dev) == GRUB_ERR_NONE)
  73. {
  74. dev->driver = driver;
  75. break;
  76. }
  77. grub_print_error ();
  78. }
  79. fdtbus_scan (dev);
  80. }
  81. }
  82. void
  83. grub_fdtbus_register (struct grub_fdtbus_driver *driver)
  84. {
  85. struct grub_fdtbus_dev *dev;
  86. grub_list_push (GRUB_AS_LIST_P (&drivers),
  87. GRUB_AS_LIST (driver));
  88. for (dev = devs; dev; dev = dev->next)
  89. if (!dev->driver && is_compatible (driver, dev->node))
  90. {
  91. if (driver->attach(dev) == GRUB_ERR_NONE)
  92. dev->driver = driver;
  93. grub_print_error ();
  94. }
  95. }
  96. void
  97. grub_fdtbus_unregister (struct grub_fdtbus_driver *driver)
  98. {
  99. grub_list_remove (GRUB_AS_LIST (driver));
  100. struct grub_fdtbus_dev *dev;
  101. for (dev = devs; dev; dev = dev->next)
  102. if (dev->driver == driver)
  103. {
  104. if (driver->detach)
  105. driver->detach(dev);
  106. dev->driver = 0;
  107. }
  108. }
  109. void
  110. grub_fdtbus_init (const void *dtb_in, grub_size_t size)
  111. {
  112. if (!dtb_in || grub_fdt_check_header (dtb_in, size) < 0)
  113. grub_fatal ("invalid FDT");
  114. dtb = dtb_in;
  115. const grub_uint32_t *prop = grub_fdt_get_prop (dtb, 0, "#address-cells", 0);
  116. if (prop)
  117. root_address_cells = grub_be_to_cpu32 (*prop);
  118. else
  119. root_address_cells = 1;
  120. prop = grub_fdt_get_prop (dtb, 0, "#size-cells", 0);
  121. if (prop)
  122. root_size_cells = grub_be_to_cpu32 (*prop);
  123. else
  124. root_size_cells = 1;
  125. fdtbus_scan (0);
  126. }
  127. static int
  128. get_address_cells (const struct grub_fdtbus_dev *dev)
  129. {
  130. const grub_uint32_t *prop;
  131. if (!dev)
  132. return root_address_cells;
  133. prop = grub_fdt_get_prop (dtb, dev->node, "#address-cells", 0);
  134. if (prop)
  135. return grub_be_to_cpu32 (*prop);
  136. return 1;
  137. }
  138. static int
  139. get_size_cells (const struct grub_fdtbus_dev *dev)
  140. {
  141. const grub_uint32_t *prop;
  142. if (!dev)
  143. return root_size_cells;
  144. prop = grub_fdt_get_prop (dtb, dev->node, "#size-cells", 0);
  145. if (prop)
  146. return grub_be_to_cpu32 (*prop);
  147. return 1;
  148. }
  149. static grub_uint64_t
  150. get64 (const grub_uint32_t *reg, grub_size_t cells)
  151. {
  152. grub_uint64_t val = 0;
  153. if (cells >= 1)
  154. val = grub_be_to_cpu32 (reg[cells - 1]);
  155. if (cells >= 2)
  156. val |= ((grub_uint64_t) grub_be_to_cpu32 (reg[cells - 2])) << 32;
  157. return val;
  158. }
  159. static volatile void *
  160. translate (const struct grub_fdtbus_dev *dev, const grub_uint32_t *reg)
  161. {
  162. volatile void *ret;
  163. const grub_uint32_t *ranges;
  164. grub_size_t ranges_size, cells_per_mapping;
  165. grub_size_t parent_address_cells, child_address_cells, child_size_cells;
  166. grub_size_t nmappings, i;
  167. if (dev == 0)
  168. {
  169. grub_uint64_t val;
  170. val = get64 (reg, root_address_cells);
  171. if (sizeof (void *) == 4 && (val >> 32))
  172. return grub_fdtbus_invalid_mapping;
  173. return (void *) (grub_addr_t) val;
  174. }
  175. ranges = grub_fdt_get_prop (dtb, dev->node, "ranges", &ranges_size);
  176. if (!ranges)
  177. return grub_fdtbus_invalid_mapping;
  178. if (ranges_size == 0)
  179. return translate (dev->parent, reg);
  180. parent_address_cells = get_address_cells (dev->parent);
  181. child_address_cells = get_address_cells (dev);
  182. child_size_cells = get_size_cells (dev);
  183. cells_per_mapping = parent_address_cells + child_address_cells + child_size_cells;
  184. nmappings = ranges_size / 4 / cells_per_mapping;
  185. for (i = 0; i < nmappings; i++)
  186. {
  187. const grub_uint32_t *child_addr = &ranges[i * cells_per_mapping];
  188. const grub_uint32_t *parent_addr = child_addr + child_address_cells;
  189. grub_uint64_t child_size = get64 (parent_addr + parent_address_cells, child_size_cells);
  190. if (child_address_cells > 2 && grub_memcmp (reg, child_addr, (child_address_cells - 2) * 4) != 0)
  191. continue;
  192. if (get64 (reg, child_address_cells) < get64 (child_addr, child_address_cells))
  193. continue;
  194. grub_uint64_t offset = get64 (reg, child_address_cells) - get64 (child_addr, child_address_cells);
  195. if (offset >= child_size)
  196. continue;
  197. ret = translate (dev->parent, parent_addr);
  198. if (grub_fdtbus_is_mapping_valid (ret))
  199. ret = (volatile char *) ret + offset;
  200. return ret;
  201. }
  202. return grub_fdtbus_invalid_mapping;
  203. }
  204. volatile void *
  205. grub_fdtbus_map_reg (const struct grub_fdtbus_dev *dev, int regno, grub_size_t *size)
  206. {
  207. grub_size_t address_cells, size_cells;
  208. address_cells = get_address_cells (dev->parent);
  209. size_cells = get_size_cells (dev->parent);
  210. const grub_uint32_t *reg = grub_fdt_get_prop (dtb, dev->node, "reg", 0);
  211. if (size && size_cells)
  212. *size = reg[(address_cells + size_cells) * regno + address_cells];
  213. if (size && !size_cells)
  214. *size = 0;
  215. return translate (dev->parent, reg + (address_cells + size_cells) * regno);
  216. }
  217. const char *
  218. grub_fdtbus_get_name (const struct grub_fdtbus_dev *dev)
  219. {
  220. return grub_fdt_get_nodename (dtb, dev->node);
  221. }
  222. const void *
  223. grub_fdtbus_get_prop (const struct grub_fdtbus_dev *dev,
  224. const char *name,
  225. grub_uint32_t *len)
  226. {
  227. return grub_fdt_get_prop (dtb, dev->node, name, len);
  228. }
  229. const void *
  230. grub_fdtbus_get_fdt (void)
  231. {
  232. return dtb;
  233. }