fdt_early.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #include <linux/types.h>
  3. #include <linux/init.h>
  4. #include <linux/libfdt.h>
  5. #include <linux/ctype.h>
  6. #include "pi.h"
  7. u64 get_kaslr_seed(uintptr_t dtb_pa)
  8. {
  9. int node, len;
  10. fdt64_t *prop;
  11. u64 ret;
  12. node = fdt_path_offset((void *)dtb_pa, "/chosen");
  13. if (node < 0)
  14. return 0;
  15. prop = fdt_getprop_w((void *)dtb_pa, node, "kaslr-seed", &len);
  16. if (!prop || len != sizeof(u64))
  17. return 0;
  18. ret = fdt64_to_cpu(*prop);
  19. *prop = 0;
  20. return ret;
  21. }
  22. /**
  23. * fdt_device_is_available - check if a device is available for use
  24. *
  25. * @fdt: pointer to the device tree blob
  26. * @node: offset of the node whose property to find
  27. *
  28. * Returns true if the status property is absent or set to "okay" or "ok",
  29. * false otherwise
  30. */
  31. static bool fdt_device_is_available(const void *fdt, int node)
  32. {
  33. const char *status;
  34. int statlen;
  35. status = fdt_getprop(fdt, node, "status", &statlen);
  36. if (!status)
  37. return true;
  38. if (statlen > 0) {
  39. if (!strcmp(status, "okay") || !strcmp(status, "ok"))
  40. return true;
  41. }
  42. return false;
  43. }
  44. /* Copy of fdt_nodename_eq_ */
  45. static int fdt_node_name_eq(const void *fdt, int offset,
  46. const char *s)
  47. {
  48. int olen;
  49. int len = strlen(s);
  50. const char *p = fdt_get_name(fdt, offset, &olen);
  51. if (!p || olen < len)
  52. /* short match */
  53. return 0;
  54. if (memcmp(p, s, len) != 0)
  55. return 0;
  56. if (p[len] == '\0')
  57. return 1;
  58. else if (!memchr(s, '@', len) && (p[len] == '@'))
  59. return 1;
  60. else
  61. return 0;
  62. }
  63. /**
  64. * isa_string_contains - check if isa string contains an extension
  65. *
  66. * @isa_str: isa string to search
  67. * @ext_name: the extension to search for
  68. *
  69. * Returns true if the extension is in the given isa string,
  70. * false otherwise
  71. */
  72. static bool isa_string_contains(const char *isa_str, const char *ext_name)
  73. {
  74. size_t i, single_end, len = strlen(ext_name);
  75. char ext_end;
  76. /* Error must contain rv32/64 */
  77. if (strlen(isa_str) < 4)
  78. return false;
  79. if (len == 1) {
  80. single_end = strcspn(isa_str, "sSxXzZ");
  81. /* Search for single chars between rv32/64 and multi-letter extensions */
  82. for (i = 4; i < single_end; i++) {
  83. if (tolower(isa_str[i]) == ext_name[0])
  84. return true;
  85. }
  86. return false;
  87. }
  88. /* Skip to start of multi-letter extensions */
  89. isa_str = strpbrk(isa_str, "sSxXzZ");
  90. while (isa_str) {
  91. if (strncasecmp(isa_str, ext_name, len) == 0) {
  92. ext_end = isa_str[len];
  93. /* Check if matches the whole extension. */
  94. if (ext_end == '\0' || ext_end == '_')
  95. return true;
  96. }
  97. /* Multi-letter extensions must be split from other multi-letter
  98. * extensions with an "_", the end of a multi-letter extension will
  99. * either be the null character or the "_" at the start of the next
  100. * multi-letter extension.
  101. */
  102. isa_str = strchr(isa_str, '_');
  103. if (isa_str)
  104. isa_str++;
  105. }
  106. return false;
  107. }
  108. /**
  109. * early_cpu_isa_ext_available - check if cpu node has an extension
  110. *
  111. * @fdt: pointer to the device tree blob
  112. * @node: offset of the cpu node
  113. * @ext_name: the extension to search for
  114. *
  115. * Returns true if the cpu node has the extension,
  116. * false otherwise
  117. */
  118. static bool early_cpu_isa_ext_available(const void *fdt, int node, const char *ext_name)
  119. {
  120. const void *prop;
  121. int len;
  122. prop = fdt_getprop(fdt, node, "riscv,isa-extensions", &len);
  123. if (prop && fdt_stringlist_contains(prop, len, ext_name))
  124. return true;
  125. prop = fdt_getprop(fdt, node, "riscv,isa", &len);
  126. if (prop && isa_string_contains(prop, ext_name))
  127. return true;
  128. return false;
  129. }
  130. /**
  131. * fdt_early_match_extension_isa - check if all cpu nodes have an extension
  132. *
  133. * @fdt: pointer to the device tree blob
  134. * @ext_name: the extension to search for
  135. *
  136. * Returns true if the all available the cpu nodes have the extension,
  137. * false otherwise
  138. */
  139. bool fdt_early_match_extension_isa(const void *fdt, const char *ext_name)
  140. {
  141. int node, parent;
  142. bool ret = false;
  143. parent = fdt_path_offset(fdt, "/cpus");
  144. if (parent < 0)
  145. return false;
  146. fdt_for_each_subnode(node, fdt, parent) {
  147. if (!fdt_node_name_eq(fdt, node, "cpu"))
  148. continue;
  149. if (!fdt_device_is_available(fdt, node))
  150. continue;
  151. if (!early_cpu_isa_ext_available(fdt, node, ext_name))
  152. return false;
  153. ret = true;
  154. }
  155. return ret;
  156. }