openfw.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /* openfw.c -- Open firmware support functions. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2003,2004,2005,2007,2008,2009 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/types.h>
  20. #include <grub/err.h>
  21. #include <grub/misc.h>
  22. #include <grub/mm.h>
  23. #include <grub/machine/kernel.h>
  24. #include <grub/ieee1275/ieee1275.h>
  25. GRUB_EXPORT(grub_devalias_iterate);
  26. GRUB_EXPORT(grub_children_iterate);
  27. GRUB_EXPORT(grub_claimmap);
  28. GRUB_EXPORT(grub_ieee1275_encode_devname);
  29. GRUB_EXPORT(grub_ieee1275_get_filename);
  30. GRUB_EXPORT(grub_halt);
  31. GRUB_EXPORT(grub_reboot);
  32. enum grub_ieee1275_parse_type
  33. {
  34. GRUB_PARSE_FILENAME,
  35. GRUB_PARSE_PARTITION,
  36. };
  37. /* Walk children of 'devpath', calling hook for each. */
  38. int
  39. grub_children_iterate (char *devpath,
  40. int (*hook) (struct grub_ieee1275_devalias *alias,
  41. void *closure),
  42. void *closure)
  43. {
  44. grub_ieee1275_phandle_t dev;
  45. grub_ieee1275_phandle_t child;
  46. char *childtype, *childpath;
  47. char *childname;
  48. int ret = 0;
  49. if (grub_ieee1275_finddevice (devpath, &dev))
  50. return 0;
  51. if (grub_ieee1275_child (dev, &child))
  52. return 0;
  53. childtype = grub_malloc (IEEE1275_MAX_PROP_LEN);
  54. if (!childtype)
  55. return 0;
  56. childpath = grub_malloc (IEEE1275_MAX_PATH_LEN);
  57. if (!childpath)
  58. {
  59. grub_free (childtype);
  60. return 0;
  61. }
  62. childname = grub_malloc (IEEE1275_MAX_PROP_LEN);
  63. if (!childname)
  64. {
  65. grub_free (childpath);
  66. grub_free (childtype);
  67. return 0;
  68. }
  69. do
  70. {
  71. struct grub_ieee1275_devalias alias;
  72. grub_ssize_t actual;
  73. if (grub_ieee1275_get_property (child, "device_type", childtype,
  74. IEEE1275_MAX_PROP_LEN, &actual))
  75. childtype[0] = 0;
  76. if (dev == child)
  77. continue;
  78. if (grub_ieee1275_package_to_path (child, childpath,
  79. IEEE1275_MAX_PATH_LEN, &actual))
  80. continue;
  81. if (grub_strcmp (devpath, childpath) == 0)
  82. continue;
  83. if (grub_ieee1275_get_property (child, "name", childname,
  84. IEEE1275_MAX_PROP_LEN, &actual))
  85. continue;
  86. alias.type = childtype;
  87. alias.path = childpath;
  88. alias.name = childname;
  89. ret = hook (&alias, closure);
  90. if (ret)
  91. break;
  92. }
  93. while (grub_ieee1275_peer (child, &child) != -1);
  94. grub_free (childname);
  95. grub_free (childpath);
  96. grub_free (childtype);
  97. return ret;
  98. }
  99. int
  100. grub_ieee1275_devices_iterate (int (*hook) (struct grub_ieee1275_devalias *alias))
  101. {
  102. auto int it_through (struct grub_ieee1275_devalias *alias);
  103. int it_through (struct grub_ieee1275_devalias *alias)
  104. {
  105. if (hook (alias))
  106. return 1;
  107. return grub_children_iterate (alias->path, it_through);
  108. }
  109. return grub_children_iterate ("/", it_through);
  110. }
  111. /* Iterate through all device aliases. This function can be used to
  112. find a device of a specific type. */
  113. int
  114. grub_devalias_iterate (int (*hook) (struct grub_ieee1275_devalias *alias,
  115. void *closure), void *closure)
  116. {
  117. grub_ieee1275_phandle_t aliases;
  118. char *aliasname, *devtype;
  119. grub_ssize_t actual;
  120. struct grub_ieee1275_devalias alias;
  121. int ret = 0;
  122. if (grub_ieee1275_finddevice ("/aliases", &aliases))
  123. return 0;
  124. aliasname = grub_malloc (IEEE1275_MAX_PROP_LEN);
  125. if (!aliasname)
  126. return 0;
  127. devtype = grub_malloc (IEEE1275_MAX_PROP_LEN);
  128. if (!devtype)
  129. {
  130. grub_free (aliasname);
  131. return 0;
  132. }
  133. /* Find the first property. */
  134. aliasname[0] = '\0';
  135. while (grub_ieee1275_next_property (aliases, aliasname, aliasname) > 0)
  136. {
  137. grub_ieee1275_phandle_t dev;
  138. grub_ssize_t pathlen;
  139. char *devpath;
  140. if (! aliasname[0])
  141. break;
  142. grub_dprintf ("devalias", "devalias name = %s\n", aliasname);
  143. grub_ieee1275_get_property_length (aliases, aliasname, &pathlen);
  144. /* The property `name' is a special case we should skip. */
  145. if (!grub_strcmp (aliasname, "name"))
  146. continue;
  147. /* Sun's OpenBoot often doesn't zero terminate the device alias
  148. strings, so we will add a NULL byte at the end explicitly. */
  149. pathlen += 1;
  150. devpath = grub_malloc (pathlen);
  151. if (! devpath)
  152. {
  153. grub_free (devtype);
  154. grub_free (aliasname);
  155. return 0;
  156. }
  157. if (grub_ieee1275_get_property (aliases, aliasname, devpath, pathlen,
  158. &actual))
  159. {
  160. grub_dprintf ("devalias", "get_property (%s) failed\n", aliasname);
  161. goto nextprop;
  162. }
  163. devpath [actual] = '\0';
  164. if (grub_ieee1275_finddevice (devpath, &dev))
  165. {
  166. grub_dprintf ("devalias", "finddevice (%s) failed\n", devpath);
  167. goto nextprop;
  168. }
  169. if (grub_ieee1275_get_property (dev, "device_type", devtype,
  170. IEEE1275_MAX_PROP_LEN, &actual))
  171. {
  172. /* NAND device don't have device_type property. */
  173. devtype[0] = 0;
  174. }
  175. alias.name = aliasname;
  176. alias.path = devpath;
  177. alias.type = devtype;
  178. ret = hook (&alias, closure);
  179. nextprop:
  180. grub_free (devpath);
  181. if (ret)
  182. break;
  183. }
  184. grub_free (devtype);
  185. grub_free (aliasname);
  186. return ret;
  187. }
  188. /* Call the "map" method of /chosen/mmu. */
  189. int
  190. grub_ieee1275_map (grub_addr_t phys, grub_addr_t virt, grub_size_t size,
  191. grub_uint32_t mode)
  192. {
  193. struct map_args {
  194. struct grub_ieee1275_common_hdr common;
  195. grub_ieee1275_cell_t method;
  196. grub_ieee1275_cell_t ihandle;
  197. grub_ieee1275_cell_t mode;
  198. grub_ieee1275_cell_t size;
  199. grub_ieee1275_cell_t virt;
  200. #ifdef GRUB_MACHINE_SPARC64
  201. grub_ieee1275_cell_t phys_high;
  202. #endif
  203. grub_ieee1275_cell_t phys_low;
  204. grub_ieee1275_cell_t catch_result;
  205. } args;
  206. INIT_IEEE1275_COMMON (&args.common, "call-method",
  207. #ifdef GRUB_MACHINE_SPARC64
  208. 7,
  209. #else
  210. 6,
  211. #endif
  212. 1);
  213. args.method = (grub_ieee1275_cell_t) "map";
  214. args.ihandle = grub_ieee1275_mmu;
  215. #ifdef GRUB_MACHINE_SPARC64
  216. args.phys_high = 0;
  217. #endif
  218. args.phys_low = phys;
  219. args.virt = virt;
  220. args.size = size;
  221. args.mode = mode; /* Format is WIMG0PP. */
  222. args.catch_result = (grub_ieee1275_cell_t) -1;
  223. if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
  224. return -1;
  225. return args.catch_result;
  226. }
  227. int
  228. grub_claimmap (grub_addr_t addr, grub_size_t size)
  229. {
  230. if (grub_ieee1275_claim (addr, size, 0, 0))
  231. return -1;
  232. if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_REAL_MODE)
  233. && grub_ieee1275_map (addr, addr, size, 0x00))
  234. {
  235. grub_printf ("map failed: address 0x%llx, size 0x%llx\n",
  236. (long long) addr, (long long) size);
  237. grub_ieee1275_release (addr, size);
  238. return -1;
  239. }
  240. return 0;
  241. }
  242. /* Get the device arguments of the Open Firmware node name `path'. */
  243. static char *
  244. grub_ieee1275_get_devargs (const char *path)
  245. {
  246. char *colon = grub_strchr (path, ':');
  247. if (! colon)
  248. return 0;
  249. return grub_strdup (colon + 1);
  250. }
  251. struct grub_ieee1275_get_devname_closure
  252. {
  253. const char *path;
  254. char *newpath;
  255. int pathlen;
  256. };
  257. static int
  258. match_alias (struct grub_ieee1275_devalias *curalias, void *closure)
  259. {
  260. struct grub_ieee1275_get_devname_closure *c = closure;
  261. /* briQ firmware can change capitalization in /chosen/bootpath. */
  262. if (! grub_strncasecmp (curalias->path, c->path, c->pathlen))
  263. {
  264. c->newpath = grub_strdup (curalias->name);
  265. return 1;
  266. }
  267. return 0;
  268. }
  269. /* Get the device path of the Open Firmware node name `path'. */
  270. static char *
  271. grub_ieee1275_get_devname (const char *path)
  272. {
  273. char *colon = grub_strchr (path, ':');
  274. struct grub_ieee1275_get_devname_closure c;
  275. c.path = path;
  276. c.newpath = 0;
  277. c.pathlen = grub_strlen (path);
  278. if (colon)
  279. c.pathlen = (int)(colon - path);
  280. /* Try to find an alias for this device. */
  281. grub_devalias_iterate (match_alias, &c);
  282. if (! c.newpath)
  283. c.newpath = grub_strndup (path, c.pathlen);
  284. return c.newpath;
  285. }
  286. static char *
  287. grub_ieee1275_parse_args (const char *path, enum grub_ieee1275_parse_type ptype)
  288. {
  289. char type[64]; /* XXX check size. */
  290. char *device = grub_ieee1275_get_devname (path);
  291. char *args = grub_ieee1275_get_devargs (path);
  292. char *ret = 0;
  293. grub_ieee1275_phandle_t dev;
  294. if (!args)
  295. /* Shouldn't happen. */
  296. return 0;
  297. /* We need to know what type of device it is in order to parse the full
  298. file path properly. */
  299. if (grub_ieee1275_finddevice (device, &dev))
  300. {
  301. grub_error (GRUB_ERR_UNKNOWN_DEVICE, "device %s not found", device);
  302. goto fail;
  303. }
  304. if (grub_ieee1275_get_property (dev, "device_type", &type, sizeof type, 0))
  305. {
  306. grub_error (GRUB_ERR_UNKNOWN_DEVICE,
  307. "device %s lacks a device_type property", device);
  308. goto fail;
  309. }
  310. if (!grub_strcmp ("block", type))
  311. {
  312. /* The syntax of the device arguments is defined in the CHRP and PReP
  313. IEEE1275 bindings: "[partition][,[filename]]". */
  314. char *comma = grub_strchr (args, ',');
  315. if (ptype == GRUB_PARSE_FILENAME)
  316. {
  317. if (comma)
  318. {
  319. char *filepath = comma + 1;
  320. /* Make sure filepath has leading backslash. */
  321. if (filepath[0] != '\\')
  322. ret = grub_xasprintf ("\\%s", filepath);
  323. else
  324. ret = grub_strdup (filepath);
  325. }
  326. }
  327. else if (ptype == GRUB_PARSE_PARTITION)
  328. {
  329. if (!comma)
  330. ret = grub_strdup (args);
  331. else
  332. ret = grub_strndup (args, (grub_size_t)(comma - args));
  333. }
  334. }
  335. else
  336. {
  337. /* XXX Handle net devices by configuring & registering a grub_net_dev
  338. here, then return its name?
  339. Example path: "net:<server ip>,<file name>,<client ip>,<gateway
  340. ip>,<bootp retries>,<tftp retries>". */
  341. grub_printf ("Unsupported type %s for device %s\n", type, device);
  342. }
  343. fail:
  344. grub_free (device);
  345. grub_free (args);
  346. return ret;
  347. }
  348. char *
  349. grub_ieee1275_get_filename (const char *path)
  350. {
  351. return grub_ieee1275_parse_args (path, GRUB_PARSE_FILENAME);
  352. }
  353. /* Convert a device name from IEEE1275 syntax to GRUB syntax. */
  354. char *
  355. grub_ieee1275_encode_devname (const char *path)
  356. {
  357. char *device = grub_ieee1275_get_devname (path);
  358. char *partition = grub_ieee1275_parse_args (path, GRUB_PARSE_PARTITION);
  359. char *encoding;
  360. if (partition && partition[0])
  361. {
  362. unsigned int partno = grub_strtoul (partition, 0, 0);
  363. if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS))
  364. /* GRUB partition 1 is OF partition 0. */
  365. partno++;
  366. encoding = grub_xasprintf ("(%s,%d)", device, partno);
  367. }
  368. else
  369. encoding = grub_xasprintf ("(%s)", device);
  370. grub_free (partition);
  371. grub_free (device);
  372. return encoding;
  373. }
  374. /* On i386, a firmware-independant grub_reboot() is provided by realmode.S. */
  375. #ifndef __i386__
  376. void
  377. grub_reboot (void)
  378. {
  379. grub_ieee1275_interpret ("reset-all", 0);
  380. }
  381. #endif
  382. void
  383. grub_halt (void)
  384. {
  385. /* Not standardized. We try three known commands. */
  386. grub_ieee1275_interpret ("shut-down", 0);
  387. grub_ieee1275_interpret ("power-off", 0);
  388. grub_ieee1275_interpret ("poweroff", 0);
  389. }