openfw.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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/ieee1275/ieee1275.h>
  24. #include <grub/net.h>
  25. enum grub_ieee1275_parse_type
  26. {
  27. GRUB_PARSE_FILENAME,
  28. GRUB_PARSE_PARTITION,
  29. GRUB_PARSE_DEVICE,
  30. GRUB_PARSE_DEVICE_TYPE
  31. };
  32. static int
  33. fill_alias (struct grub_ieee1275_devalias *alias)
  34. {
  35. grub_ssize_t actual;
  36. if (grub_ieee1275_get_property (alias->phandle, "device_type", alias->type,
  37. IEEE1275_MAX_PROP_LEN, &actual))
  38. alias->type[0] = 0;
  39. if (alias->parent_dev == alias->phandle)
  40. return 0;
  41. if (grub_ieee1275_package_to_path (alias->phandle, alias->path,
  42. IEEE1275_MAX_PATH_LEN, &actual))
  43. return 0;
  44. if (grub_strcmp (alias->parent_path, alias->path) == 0)
  45. return 0;
  46. if (grub_ieee1275_get_property (alias->phandle, "name", alias->name,
  47. IEEE1275_MAX_PROP_LEN, &actual))
  48. return 0;
  49. grub_dprintf ("devalias", "device path=%s\n", alias->path);
  50. return 1;
  51. }
  52. void
  53. grub_ieee1275_devalias_free (struct grub_ieee1275_devalias *alias)
  54. {
  55. grub_free (alias->name);
  56. grub_free (alias->type);
  57. grub_free (alias->path);
  58. grub_free (alias->parent_path);
  59. alias->name = 0;
  60. alias->type = 0;
  61. alias->path = 0;
  62. alias->parent_path = 0;
  63. alias->phandle = GRUB_IEEE1275_PHANDLE_INVALID;
  64. }
  65. void
  66. grub_ieee1275_children_peer (struct grub_ieee1275_devalias *alias)
  67. {
  68. while (grub_ieee1275_peer (alias->phandle, &alias->phandle) != -1)
  69. if (fill_alias (alias))
  70. return;
  71. grub_ieee1275_devalias_free (alias);
  72. }
  73. void
  74. grub_ieee1275_children_first (const char *devpath,
  75. struct grub_ieee1275_devalias *alias)
  76. {
  77. grub_ieee1275_phandle_t dev;
  78. grub_dprintf ("devalias", "iterating children of %s\n",
  79. devpath);
  80. alias->name = 0;
  81. alias->path = 0;
  82. alias->parent_path = 0;
  83. alias->type = 0;
  84. if (grub_ieee1275_finddevice (devpath, &dev))
  85. return;
  86. if (grub_ieee1275_child (dev, &alias->phandle))
  87. return;
  88. alias->type = grub_malloc (IEEE1275_MAX_PROP_LEN);
  89. if (!alias->type)
  90. return;
  91. alias->path = grub_malloc (IEEE1275_MAX_PATH_LEN);
  92. if (!alias->path)
  93. {
  94. grub_free (alias->type);
  95. return;
  96. }
  97. alias->parent_path = grub_strdup (devpath);
  98. if (!alias->parent_path)
  99. {
  100. grub_free (alias->path);
  101. grub_free (alias->type);
  102. return;
  103. }
  104. alias->name = grub_malloc (IEEE1275_MAX_PROP_LEN);
  105. if (!alias->name)
  106. {
  107. grub_free (alias->path);
  108. grub_free (alias->type);
  109. grub_free (alias->parent_path);
  110. return;
  111. }
  112. if (!fill_alias (alias))
  113. grub_ieee1275_children_peer (alias);
  114. }
  115. static int
  116. iterate_recursively (const char *path,
  117. int (*hook) (struct grub_ieee1275_devalias *alias))
  118. {
  119. struct grub_ieee1275_devalias alias;
  120. int ret = 0;
  121. FOR_IEEE1275_DEVCHILDREN(path, alias)
  122. {
  123. ret = hook (&alias);
  124. if (ret)
  125. break;
  126. ret = iterate_recursively (alias.path, hook);
  127. if (ret)
  128. break;
  129. }
  130. grub_ieee1275_devalias_free (&alias);
  131. return ret;
  132. }
  133. int
  134. grub_ieee1275_devices_iterate (int (*hook) (struct grub_ieee1275_devalias *alias))
  135. {
  136. return iterate_recursively ("/", hook);
  137. }
  138. void
  139. grub_ieee1275_devalias_init_iterator (struct grub_ieee1275_devalias *alias)
  140. {
  141. alias->name = 0;
  142. alias->path = 0;
  143. alias->parent_path = 0;
  144. alias->type = 0;
  145. grub_dprintf ("devalias", "iterating aliases\n");
  146. if (grub_ieee1275_finddevice ("/aliases", &alias->parent_dev))
  147. return;
  148. alias->name = grub_malloc (IEEE1275_MAX_PROP_LEN);
  149. if (!alias->name)
  150. return;
  151. alias->type = grub_malloc (IEEE1275_MAX_PROP_LEN);
  152. if (!alias->type)
  153. {
  154. grub_free (alias->name);
  155. alias->name = 0;
  156. return;
  157. }
  158. alias->name[0] = '\0';
  159. }
  160. int
  161. grub_ieee1275_devalias_next (struct grub_ieee1275_devalias *alias)
  162. {
  163. if (!alias->name)
  164. return 0;
  165. while (1)
  166. {
  167. grub_ssize_t pathlen;
  168. grub_ssize_t actual;
  169. char *tmp;
  170. if (alias->path)
  171. {
  172. grub_free (alias->path);
  173. alias->path = 0;
  174. }
  175. tmp = grub_strdup (alias->name);
  176. if (grub_ieee1275_next_property (alias->parent_dev, tmp,
  177. alias->name) <= 0)
  178. {
  179. grub_free (tmp);
  180. grub_ieee1275_devalias_free (alias);
  181. return 0;
  182. }
  183. grub_free (tmp);
  184. grub_dprintf ("devalias", "devalias name = %s\n", alias->name);
  185. grub_ieee1275_get_property_length (alias->parent_dev, alias->name, &pathlen);
  186. /* The property `name' is a special case we should skip. */
  187. if (grub_strcmp (alias->name, "name") == 0)
  188. continue;
  189. /* Sun's OpenBoot often doesn't zero terminate the device alias
  190. strings, so we will add a NULL byte at the end explicitly. */
  191. pathlen += 1;
  192. alias->path = grub_malloc (pathlen + 1);
  193. if (! alias->path)
  194. {
  195. grub_ieee1275_devalias_free (alias);
  196. return 0;
  197. }
  198. if (grub_ieee1275_get_property (alias->parent_dev, alias->name, alias->path,
  199. pathlen, &actual) || actual < 0)
  200. {
  201. grub_dprintf ("devalias", "get_property (%s) failed\n", alias->name);
  202. grub_free (alias->path);
  203. continue;
  204. }
  205. if (actual > pathlen)
  206. actual = pathlen;
  207. alias->path[actual] = '\0';
  208. alias->path[pathlen] = '\0';
  209. if (grub_ieee1275_finddevice (alias->path, &alias->phandle))
  210. {
  211. grub_dprintf ("devalias", "finddevice (%s) failed\n", alias->path);
  212. grub_free (alias->path);
  213. alias->path = 0;
  214. continue;
  215. }
  216. if (grub_ieee1275_get_property (alias->phandle, "device_type", alias->type,
  217. IEEE1275_MAX_PROP_LEN, &actual))
  218. {
  219. /* NAND device don't have device_type property. */
  220. alias->type[0] = 0;
  221. }
  222. return 1;
  223. }
  224. }
  225. /* Call the "map" method of /chosen/mmu. */
  226. int
  227. grub_ieee1275_map (grub_addr_t phys, grub_addr_t virt, grub_size_t size,
  228. grub_uint32_t mode)
  229. {
  230. struct map_args {
  231. struct grub_ieee1275_common_hdr common;
  232. grub_ieee1275_cell_t method;
  233. grub_ieee1275_cell_t ihandle;
  234. grub_ieee1275_cell_t mode;
  235. grub_ieee1275_cell_t size;
  236. grub_ieee1275_cell_t virt;
  237. #ifdef __sparc__
  238. grub_ieee1275_cell_t phys_high;
  239. #endif
  240. grub_ieee1275_cell_t phys_low;
  241. grub_ieee1275_cell_t catch_result;
  242. } args;
  243. INIT_IEEE1275_COMMON (&args.common, "call-method",
  244. #ifdef __sparc__
  245. 7,
  246. #else
  247. 6,
  248. #endif
  249. 1);
  250. args.method = (grub_ieee1275_cell_t) "map";
  251. args.ihandle = grub_ieee1275_mmu;
  252. #ifdef __sparc__
  253. args.phys_high = 0;
  254. #endif
  255. args.phys_low = phys;
  256. args.virt = virt;
  257. args.size = size;
  258. args.mode = mode; /* Format is WIMG0PP. */
  259. args.catch_result = (grub_ieee1275_cell_t) -1;
  260. if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
  261. return -1;
  262. return args.catch_result;
  263. }
  264. grub_err_t
  265. grub_claimmap (grub_addr_t addr, grub_size_t size)
  266. {
  267. if (grub_ieee1275_claim (addr, size, 0, 0))
  268. return -1;
  269. if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_REAL_MODE)
  270. && grub_ieee1275_map (addr, addr, size, 0x00))
  271. {
  272. grub_error (GRUB_ERR_OUT_OF_MEMORY, "map failed: address 0x%llx, size 0x%llx\n",
  273. (long long) addr, (long long) size);
  274. grub_ieee1275_release (addr, size);
  275. return grub_errno;
  276. }
  277. return GRUB_ERR_NONE;
  278. }
  279. /* Get the device arguments of the Open Firmware node name `path'. */
  280. static char *
  281. grub_ieee1275_get_devargs (const char *path)
  282. {
  283. char *colon = grub_strchr (path, ':');
  284. if (! colon)
  285. return 0;
  286. return grub_strdup (colon + 1);
  287. }
  288. /* Get the device path of the Open Firmware node name `path'. */
  289. char *
  290. grub_ieee1275_get_devname (const char *path)
  291. {
  292. char *colon = grub_strchr (path, ':');
  293. int pathlen = grub_strlen (path);
  294. struct grub_ieee1275_devalias curalias;
  295. if (colon)
  296. pathlen = (int)(colon - path);
  297. /* Try to find an alias for this device. */
  298. FOR_IEEE1275_DEVALIASES (curalias)
  299. /* briQ firmware can change capitalization in /chosen/bootpath. */
  300. if (grub_strncasecmp (curalias.path, path, pathlen) == 0
  301. && curalias.path[pathlen] == 0)
  302. {
  303. char *newpath;
  304. newpath = grub_strdup (curalias.name);
  305. grub_ieee1275_devalias_free (&curalias);
  306. return newpath;
  307. }
  308. return grub_strndup (path, pathlen);
  309. }
  310. static char *
  311. grub_ieee1275_parse_args (const char *path, enum grub_ieee1275_parse_type ptype)
  312. {
  313. char type[64]; /* XXX check size. */
  314. char *device = grub_ieee1275_get_devname (path);
  315. char *ret = 0;
  316. grub_ieee1275_phandle_t dev;
  317. /* We need to know what type of device it is in order to parse the full
  318. file path properly. */
  319. if (grub_ieee1275_finddevice (device, &dev))
  320. {
  321. grub_error (GRUB_ERR_UNKNOWN_DEVICE, "device %s not found", device);
  322. goto fail;
  323. }
  324. if (grub_ieee1275_get_property (dev, "device_type", &type, sizeof type, 0))
  325. {
  326. grub_error (GRUB_ERR_UNKNOWN_DEVICE,
  327. "device %s lacks a device_type property", device);
  328. goto fail;
  329. }
  330. switch (ptype)
  331. {
  332. case GRUB_PARSE_DEVICE:
  333. ret = grub_strdup (device);
  334. break;
  335. case GRUB_PARSE_DEVICE_TYPE:
  336. ret = grub_strdup (type);
  337. break;
  338. case GRUB_PARSE_FILENAME:
  339. {
  340. char *comma;
  341. char *args;
  342. args = grub_ieee1275_get_devargs (path);
  343. if (!args)
  344. /* Shouldn't happen. */
  345. return 0;
  346. /* The syntax of the device arguments is defined in the CHRP and PReP
  347. IEEE1275 bindings: "[partition][,[filename]]". */
  348. comma = grub_strchr (args, ',');
  349. if (comma)
  350. {
  351. char *filepath = comma + 1;
  352. /* Make sure filepath has leading backslash. */
  353. if (filepath[0] != '\\')
  354. ret = grub_xasprintf ("\\%s", filepath);
  355. else
  356. ret = grub_strdup (filepath);
  357. }
  358. grub_free (args);
  359. }
  360. break;
  361. case GRUB_PARSE_PARTITION:
  362. {
  363. char *comma;
  364. char *args;
  365. if (grub_strcmp ("block", type) != 0)
  366. goto unknown;
  367. args = grub_ieee1275_get_devargs (path);
  368. if (!args)
  369. /* Shouldn't happen. */
  370. return 0;
  371. comma = grub_strchr (args, ',');
  372. if (!comma)
  373. ret = grub_strdup (args);
  374. else
  375. ret = grub_strndup (args, (grub_size_t)(comma - args));
  376. /* Consistently provide numbered partitions to GRUB.
  377. OpenBOOT traditionally uses alphabetical partition
  378. specifiers. */
  379. if (ret[0] >= 'a' && ret[0] <= 'z')
  380. ret[0] = '1' + (ret[0] - 'a');
  381. grub_free (args);
  382. }
  383. break;
  384. default:
  385. unknown:
  386. grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
  387. "unsupported type %s for device %s", type, device);
  388. }
  389. fail:
  390. grub_free (device);
  391. return ret;
  392. }
  393. char *
  394. grub_ieee1275_get_device_type (const char *path)
  395. {
  396. return grub_ieee1275_parse_args (path, GRUB_PARSE_DEVICE_TYPE);
  397. }
  398. char *
  399. grub_ieee1275_get_aliasdevname (const char *path)
  400. {
  401. return grub_ieee1275_parse_args (path, GRUB_PARSE_DEVICE);
  402. }
  403. char *
  404. grub_ieee1275_get_filename (const char *path)
  405. {
  406. return grub_ieee1275_parse_args (path, GRUB_PARSE_FILENAME);
  407. }
  408. /* Convert a device name from IEEE1275 syntax to GRUB syntax. */
  409. char *
  410. grub_ieee1275_encode_devname (const char *path)
  411. {
  412. char *device = grub_ieee1275_get_devname (path);
  413. char *partition;
  414. char *encoding;
  415. char *optr;
  416. const char *iptr;
  417. if (! device)
  418. return 0;
  419. encoding = grub_malloc (sizeof ("ieee1275/") + 2 * grub_strlen (device)
  420. + sizeof (",XXXXXXXXXXXX"));
  421. if (!encoding)
  422. {
  423. grub_free (device);
  424. return 0;
  425. }
  426. partition = grub_ieee1275_parse_args (path, GRUB_PARSE_PARTITION);
  427. optr = grub_stpcpy (encoding, "ieee1275/");
  428. for (iptr = device; *iptr; )
  429. {
  430. if (*iptr == ',')
  431. *optr++ ='\\';
  432. *optr++ = *iptr++;
  433. }
  434. if (partition && partition[0])
  435. {
  436. unsigned int partno = grub_strtoul (partition, 0, 0);
  437. *optr++ = ',';
  438. if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_0_BASED_PARTITIONS))
  439. /* GRUB partition 1 is OF partition 0. */
  440. partno++;
  441. grub_snprintf (optr, sizeof ("XXXXXXXXXXXX"), "%d", partno);
  442. }
  443. else
  444. *optr = '\0';
  445. grub_free (partition);
  446. grub_free (device);
  447. return encoding;
  448. }
  449. /* Resolve aliases. */
  450. char *
  451. grub_ieee1275_canonicalise_devname (const char *path)
  452. {
  453. struct canon_args
  454. {
  455. struct grub_ieee1275_common_hdr common;
  456. grub_ieee1275_cell_t path;
  457. grub_ieee1275_cell_t buf;
  458. grub_ieee1275_cell_t inlen;
  459. grub_ieee1275_cell_t outlen;
  460. }
  461. args;
  462. char *buf = NULL;
  463. grub_size_t bufsize = 64;
  464. int i;
  465. for (i = 0; i < 2; i++)
  466. {
  467. grub_free (buf);
  468. buf = grub_malloc (bufsize);
  469. if (!buf)
  470. return NULL;
  471. INIT_IEEE1275_COMMON (&args.common, "canon", 3, 1);
  472. args.path = (grub_ieee1275_cell_t) path;
  473. args.buf = (grub_ieee1275_cell_t) buf;
  474. args.inlen = (grub_ieee1275_cell_t) (bufsize - 1);
  475. if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
  476. return 0;
  477. if (args.outlen > bufsize - 1)
  478. {
  479. bufsize = args.outlen + 2;
  480. continue;
  481. }
  482. return buf;
  483. }
  484. /* Shouldn't reach here. */
  485. grub_free (buf);
  486. return NULL;
  487. }
  488. char *
  489. grub_ieee1275_get_boot_dev (void)
  490. {
  491. char *bootpath;
  492. grub_ssize_t bootpath_size;
  493. if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, "bootpath",
  494. &bootpath_size)
  495. || bootpath_size <= 0)
  496. {
  497. /* Should never happen. */
  498. grub_printf ("/chosen/bootpath property missing!\n");
  499. return NULL;
  500. }
  501. bootpath = (char *) grub_malloc ((grub_size_t) bootpath_size + 64);
  502. if (! bootpath)
  503. {
  504. grub_print_error ();
  505. return NULL;
  506. }
  507. grub_ieee1275_get_property (grub_ieee1275_chosen, "bootpath", bootpath,
  508. (grub_size_t) bootpath_size + 1, 0);
  509. bootpath[bootpath_size] = '\0';
  510. return bootpath;
  511. }