boothart.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Check RISC-V boot hart ID
  4. *
  5. * Copyright 2022, Heinrich Schuchardt <xypron.glpk@gmx.de>
  6. *
  7. * This test program reads the boot HART ID both from the device-tree from the
  8. * RISCV_EFI_BOOT_PROTOCOL and writes both values to the console.
  9. */
  10. #include <efi_api.h>
  11. #include <efi_riscv.h>
  12. #include <linux/libfdt.h>
  13. static const efi_guid_t riscv_efi_boot_protocol_guid =
  14. RISCV_EFI_BOOT_PROTOCOL_GUID;
  15. static const efi_guid_t fdt_guid = EFI_FDT_GUID;
  16. static struct efi_system_table *systable;
  17. static struct efi_boot_services *boottime;
  18. static struct efi_simple_text_output_protocol *con_out;
  19. static const char *fdt;
  20. /**
  21. * Print an unsigned 32bit value as decimal number to an u16 string
  22. *
  23. * @value: value to be printed
  24. * @buf: pointer to buffer address
  25. */
  26. static void uint2dec(u32 value, u16 *buf)
  27. {
  28. u16 *pos = buf;
  29. int i;
  30. u16 c;
  31. u64 f;
  32. /*
  33. * Increment by .5 and multiply with
  34. * (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC
  35. * to move the first digit to bit 60-63.
  36. */
  37. f = 0x225C17D0;
  38. f += (0x9B5A52DULL * value) >> 28;
  39. f += 0x44B82FA0ULL * value;
  40. for (i = 0; i < 10; ++i) {
  41. /* Write current digit */
  42. c = f >> 60;
  43. if (c || pos != buf)
  44. *pos++ = c + '0';
  45. /* Eliminate current digit */
  46. f &= 0xfffffffffffffff;
  47. /* Get next digit */
  48. f *= 0xaULL;
  49. }
  50. if (pos == buf)
  51. *pos++ = '0';
  52. *pos = 0;
  53. }
  54. /**
  55. * f2h() - convert FDT value to host endianness.
  56. *
  57. * UEFI code is always low endian. The FDT is big endian.
  58. *
  59. * @val: FDT value
  60. * Return: converted value
  61. */
  62. static uint32_t f2h(fdt32_t val)
  63. {
  64. char *buf = (char *)&val;
  65. char i;
  66. /* Swap the bytes */
  67. i = buf[0]; buf[0] = buf[3]; buf[3] = i;
  68. i = buf[1]; buf[1] = buf[2]; buf[2] = i;
  69. return val;
  70. }
  71. /**
  72. * memcomp() - compare two memory buffers
  73. *
  74. * s1: first buffer
  75. * s2: second buffer
  76. * n: size of buffers
  77. * Return: 0 if both buffers have the same content
  78. */
  79. static int memcomp(const void *s1, const void *s2, size_t n)
  80. {
  81. const char *pos1 = s1, *pos2 = s2;
  82. for (size_t count = 0; count < n ; ++pos1, ++pos2, --count) {
  83. if (*pos1 != *pos2)
  84. return *pos1 - *pos2;
  85. }
  86. return 0;
  87. }
  88. /**
  89. * strcomp() - compare to strings
  90. *
  91. * @buf1: first string
  92. * @buf2: second string
  93. * Return: 0 if both strings are the same
  94. */
  95. static int strcomp(const char *buf1, const char *buf2)
  96. {
  97. for (; *buf1 || *buf2; ++buf1, ++buf2) {
  98. if (*buf1 != *buf2)
  99. return *buf1 - *buf2;
  100. }
  101. return 0;
  102. }
  103. /**
  104. * get_property() - return value of a property of an FDT node
  105. *
  106. * A property of the root node or one of its direct children can be
  107. * retrieved.
  108. *
  109. * @property name of the property
  110. * @node name of the node or NULL for root node
  111. * Return: value of the property
  112. */
  113. static char *get_property(const char *property, const char *node)
  114. {
  115. struct fdt_header *header = (struct fdt_header *)fdt;
  116. const fdt32_t *end;
  117. const fdt32_t *pos;
  118. const char *strings;
  119. size_t level = 0;
  120. const char *nodelabel = NULL;
  121. if (!header) {
  122. con_out->output_string(con_out, u"Missing device tree\r\n");
  123. return NULL;
  124. }
  125. if (f2h(header->magic) != FDT_MAGIC) {
  126. con_out->output_string(con_out, u"Wrong device tree magic\r\n");
  127. return NULL;
  128. }
  129. pos = (fdt32_t *)(fdt + f2h(header->off_dt_struct));
  130. end = &pos[f2h(header->totalsize) >> 2];
  131. strings = fdt + f2h(header->off_dt_strings);
  132. for (; pos < end;) {
  133. switch (f2h(pos[0])) {
  134. case FDT_BEGIN_NODE: {
  135. const char *c = (char *)&pos[1];
  136. size_t i;
  137. if (level == 1)
  138. nodelabel = c;
  139. ++level;
  140. for (i = 0; c[i]; ++i)
  141. ;
  142. pos = &pos[2 + (i >> 2)];
  143. break;
  144. }
  145. case FDT_PROP: {
  146. struct fdt_property *prop = (struct fdt_property *)pos;
  147. const char *label = &strings[f2h(prop->nameoff)];
  148. efi_status_t ret;
  149. /* Check if this is the property to be returned */
  150. if (!strcomp(property, label) &&
  151. ((level == 1 && !node) ||
  152. (level == 2 && node &&
  153. !strcomp(node, nodelabel)))) {
  154. char *str;
  155. efi_uintn_t len = f2h(prop->len);
  156. if (!len)
  157. return NULL;
  158. /*
  159. * The string might not be 0 terminated.
  160. * It is safer to make a copy.
  161. */
  162. ret = boottime->allocate_pool(
  163. EFI_LOADER_DATA, len + 1,
  164. (void **)&str);
  165. if (ret != EFI_SUCCESS) {
  166. con_out->output_string(
  167. con_out,
  168. u"AllocatePool failed\r\n");
  169. return NULL;
  170. }
  171. boottime->copy_mem(str, &pos[3], len);
  172. str[len] = 0;
  173. return str;
  174. }
  175. pos = &pos[3 + ((f2h(prop->len) + 3) >> 2)];
  176. break;
  177. }
  178. case FDT_NOP:
  179. ++pos;
  180. break;
  181. case FDT_END_NODE:
  182. --level;
  183. ++pos;
  184. break;
  185. case FDT_END:
  186. return NULL;
  187. default:
  188. con_out->output_string(
  189. con_out, u"Invalid device tree token\r\n");
  190. return NULL;
  191. }
  192. }
  193. con_out->output_string(
  194. con_out, u"Missing FDT_END token\r\n");
  195. return NULL;
  196. }
  197. /**
  198. * get_config_table() - get configuration table
  199. *
  200. * @guid: table GUID
  201. * Return: pointer to table or NULL
  202. */
  203. static void *get_config_table(const efi_guid_t *guid)
  204. {
  205. size_t i;
  206. for (i = 0; i < systable->nr_tables; i++) {
  207. if (!memcomp(guid, &systable->tables[i].guid, 16))
  208. return systable->tables[i].table;
  209. }
  210. return NULL;
  211. }
  212. /**
  213. * fdt_get_hart() - get hart ID via RISC-V device-tree
  214. *
  215. * @hartid: boot hart ID
  216. * Return: status code
  217. */
  218. static efi_status_t fdt_get_hart(efi_uintn_t *hartid)
  219. {
  220. char *str;
  221. fdt = get_config_table(&fdt_guid);
  222. if (!fdt) {
  223. con_out->output_string(con_out, u"Missing device tree\r\n");
  224. return EFI_NOT_FOUND;
  225. }
  226. str = get_property("boot-hartid", "chosen");
  227. if (!str) {
  228. con_out->output_string(con_out,
  229. u"/chosen/boot-hartid missing\r\n");
  230. return EFI_NOT_FOUND;
  231. }
  232. *hartid = f2h(*(fdt32_t *)str);
  233. boottime->free_pool(str);
  234. return EFI_SUCCESS;
  235. }
  236. /**
  237. * prot_get_hart() - get hart ID via RISC-V Boot Protocol
  238. *
  239. * @hartid: boot hart ID
  240. * Return: status code
  241. */
  242. static efi_status_t prot_get_hart(efi_uintn_t *hartid)
  243. {
  244. efi_status_t ret;
  245. struct riscv_efi_boot_protocol *prot;
  246. /* Get RISC-V boot protocol */
  247. ret = boottime->locate_protocol(&riscv_efi_boot_protocol_guid, NULL,
  248. (void **)&prot);
  249. if (ret != EFI_SUCCESS) {
  250. con_out->output_string(
  251. con_out, u"RISC-V Boot Protocol not available\r\n");
  252. return ret;
  253. }
  254. /* Get boot hart ID from EFI protocol */
  255. ret = prot->get_boot_hartid(prot, hartid);
  256. if (ret != EFI_SUCCESS)
  257. con_out->output_string(con_out,
  258. u"Could not retrieve boot hart ID\r\n");
  259. return ret;
  260. }
  261. /**
  262. * efi_main() - entry point of the EFI application.
  263. *
  264. * @handle: handle of the loaded image
  265. * @systab: system table
  266. * Return: status code
  267. */
  268. efi_status_t EFIAPI efi_main(efi_handle_t handle,
  269. struct efi_system_table *systab)
  270. {
  271. efi_status_t ret;
  272. efi_uintn_t hartid;
  273. u16 buf[16];
  274. systable = systab;
  275. boottime = systable->boottime;
  276. con_out = systable->con_out;
  277. con_out->output_string(con_out,
  278. u"\r\nBoot hart ID\r\n------------\r\n\r\n");
  279. ret = fdt_get_hart(&hartid);
  280. if (ret == EFI_SUCCESS) {
  281. con_out->output_string(con_out, u"Device-tree: ");
  282. uint2dec(hartid, buf);
  283. con_out->output_string(con_out, buf);
  284. con_out->output_string(con_out, u"\r\n");
  285. }
  286. ret = prot_get_hart(&hartid);
  287. if (ret == EFI_SUCCESS) {
  288. con_out->output_string(con_out, u"RISCV_EFI_BOOT_PROTOCOL: ");
  289. uint2dec(hartid, buf);
  290. con_out->output_string(con_out, buf);
  291. con_out->output_string(con_out, u"\r\n");
  292. }
  293. con_out->output_string(con_out, u"\r\n");
  294. boottime->exit(handle, EFI_SUCCESS, 0, NULL);
  295. /* We should never arrive here */
  296. return EFI_SUCCESS;
  297. }