]>
Commit | Line | Data |
---|---|---|
e56cdf21 | 1 | /* ofdisk.c - Open Firmware disk access. */ |
2 | /* | |
4b13b216 | 3 | * GRUB -- GRand Unified Bootloader |
58bc8bd5 | 4 | * Copyright (C) 2004,2006,2007,2008,2009 Free Software Foundation, Inc. |
e56cdf21 | 5 | * |
5a79f472 | 6 | * GRUB is free software: you can redistribute it and/or modify |
e56cdf21 | 7 | * it under the terms of the GNU General Public License as published by |
5a79f472 | 8 | * the Free Software Foundation, either version 3 of the License, or |
e56cdf21 | 9 | * (at your option) any later version. |
10 | * | |
5a79f472 | 11 | * GRUB is distributed in the hope that it will be useful, |
e56cdf21 | 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 | |
5a79f472 | 17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. |
e56cdf21 | 18 | */ |
19 | ||
4b13b216 | 20 | #include <grub/misc.h> |
21 | #include <grub/disk.h> | |
22 | #include <grub/mm.h> | |
3be7266d | 23 | #include <grub/ieee1275/ieee1275.h> |
24 | #include <grub/ieee1275/ofdisk.h> | |
9c4b5c13 | 25 | #include <grub/i18n.h> |
e56cdf21 | 26 | |
b2a30ac5 VS |
27 | static char *last_devpath; |
28 | static grub_ieee1275_ihandle_t last_ihandle; | |
29 | ||
f5dbbca9 | 30 | struct ofdisk_hash_ent |
31 | { | |
32 | char *devpath; | |
b2a30ac5 VS |
33 | /* Pointer to shortest available name on nodes representing canonical names, |
34 | otherwise NULL. */ | |
35 | const char *shortest; | |
f5dbbca9 | 36 | struct ofdisk_hash_ent *next; |
37 | }; | |
38 | ||
39 | #define OFDISK_HASH_SZ 8 | |
40 | static struct ofdisk_hash_ent *ofdisk_hash[OFDISK_HASH_SZ]; | |
41 | ||
42 | static int | |
43 | ofdisk_hash_fn (const char *devpath) | |
44 | { | |
45 | int hash = 0; | |
46 | while (*devpath) | |
47 | hash ^= *devpath++; | |
48 | return (hash & (OFDISK_HASH_SZ - 1)); | |
49 | } | |
50 | ||
51 | static struct ofdisk_hash_ent * | |
52 | ofdisk_hash_find (const char *devpath) | |
53 | { | |
54 | struct ofdisk_hash_ent *p = ofdisk_hash[ofdisk_hash_fn(devpath)]; | |
55 | ||
56 | while (p) | |
57 | { | |
58 | if (!grub_strcmp (p->devpath, devpath)) | |
59 | break; | |
60 | p = p->next; | |
61 | } | |
62 | return p; | |
63 | } | |
64 | ||
65 | static struct ofdisk_hash_ent * | |
b44a558c | 66 | ofdisk_hash_add_real (char *devpath) |
f5dbbca9 | 67 | { |
b44a558c | 68 | struct ofdisk_hash_ent *p; |
f5dbbca9 | 69 | struct ofdisk_hash_ent **head = &ofdisk_hash[ofdisk_hash_fn(devpath)]; |
b2a30ac5 VS |
70 | |
71 | p = grub_malloc(sizeof (*p)); | |
72 | if (!p) | |
73 | return NULL; | |
74 | ||
75 | p->devpath = devpath; | |
76 | p->next = *head; | |
77 | p->shortest = 0; | |
78 | *head = p; | |
b44a558c VS |
79 | return p; |
80 | } | |
81 | ||
82 | static struct ofdisk_hash_ent * | |
83 | ofdisk_hash_add (char *devpath, char *curcan) | |
84 | { | |
85 | struct ofdisk_hash_ent *p, *pcan; | |
86 | ||
87 | p = ofdisk_hash_add_real (devpath); | |
88 | ||
89 | grub_dprintf ("disk", "devpath = %s, canonical = %s\n", devpath, curcan); | |
b2a30ac5 | 90 | |
b2a30ac5 VS |
91 | if (!curcan) |
92 | { | |
b44a558c | 93 | p->shortest = devpath; |
b2a30ac5 VS |
94 | return p; |
95 | } | |
96 | ||
97 | pcan = ofdisk_hash_find (curcan); | |
98 | if (!pcan) | |
b44a558c | 99 | pcan = ofdisk_hash_add_real (curcan); |
b2a30ac5 VS |
100 | else |
101 | grub_free (curcan); | |
f5dbbca9 | 102 | |
b2a30ac5 VS |
103 | if (!pcan) |
104 | grub_errno = GRUB_ERR_NONE; | |
105 | else | |
f5dbbca9 | 106 | { |
b2a30ac5 VS |
107 | if (!pcan->shortest |
108 | || grub_strlen (pcan->shortest) > grub_strlen (devpath)) | |
109 | pcan->shortest = devpath; | |
f5dbbca9 | 110 | } |
b2a30ac5 | 111 | |
f5dbbca9 | 112 | return p; |
113 | } | |
114 | ||
b2a30ac5 VS |
115 | static void |
116 | scan (void) | |
e56cdf21 | 117 | { |
bf293dec | 118 | auto int dev_iterate_real (const char *name, const char *path); |
fba51f48 | 119 | |
bf293dec | 120 | int dev_iterate_real (const char *name, const char *path) |
e56cdf21 | 121 | { |
b2a30ac5 VS |
122 | struct ofdisk_hash_ent *op; |
123 | ||
bf293dec VS |
124 | grub_dprintf ("disk", "disk name = %s, path = %s\n", name, |
125 | path); | |
b2a30ac5 | 126 | |
bf293dec | 127 | op = ofdisk_hash_find (path); |
b2a30ac5 | 128 | if (!op) |
25638629 | 129 | { |
bf293dec VS |
130 | char *name_dup = grub_strdup (name); |
131 | char *can = grub_strdup (path); | |
132 | if (!name_dup || !can) | |
25638629 | 133 | { |
b2a30ac5 | 134 | grub_errno = GRUB_ERR_NONE; |
bf293dec | 135 | grub_free (name_dup); |
b44a558c | 136 | grub_free (can); |
25638629 | 137 | return 0; |
138 | } | |
bf293dec | 139 | op = ofdisk_hash_add (name_dup, can); |
b2a30ac5 VS |
140 | } |
141 | return 0; | |
142 | } | |
25638629 | 143 | |
0ec82090 VS |
144 | auto int dev_iterate_alias (struct grub_ieee1275_devalias *alias); |
145 | int dev_iterate_alias (struct grub_ieee1275_devalias *alias) | |
146 | { | |
bf293dec VS |
147 | if (grub_strcmp (alias->type, "block") != 0) |
148 | return 0; | |
149 | return dev_iterate_real (alias->name, alias->path); | |
0ec82090 VS |
150 | } |
151 | ||
152 | auto int dev_iterate (struct grub_ieee1275_devalias *alias); | |
153 | int dev_iterate (struct grub_ieee1275_devalias *alias) | |
154 | { | |
bf293dec VS |
155 | if (grub_strcmp (alias->type, "vscsi") == 0) |
156 | { | |
157 | static grub_ieee1275_ihandle_t ihandle; | |
158 | struct set_color_args | |
159 | { | |
160 | struct grub_ieee1275_common_hdr common; | |
161 | grub_ieee1275_cell_t method; | |
162 | grub_ieee1275_cell_t ihandle; | |
163 | grub_ieee1275_cell_t catch_result; | |
164 | grub_ieee1275_cell_t nentries; | |
165 | grub_ieee1275_cell_t table; | |
166 | } | |
167 | args; | |
168 | char *buf, *bufptr; | |
169 | unsigned i; | |
170 | ||
171 | if (grub_ieee1275_open (alias->path, &ihandle)) | |
172 | return 0; | |
173 | ||
174 | INIT_IEEE1275_COMMON (&args.common, "call-method", 2, 3); | |
175 | args.method = (grub_ieee1275_cell_t) "vscsi-report-luns"; | |
176 | args.ihandle = ihandle; | |
177 | args.table = 0; | |
178 | args.nentries = 0; | |
179 | ||
180 | if (IEEE1275_CALL_ENTRY_FN (&args) == -1) | |
181 | { | |
182 | grub_ieee1275_close (ihandle); | |
183 | return 0; | |
184 | } | |
185 | ||
186 | buf = grub_malloc (grub_strlen (alias->path) + 32); | |
187 | if (!buf) | |
188 | return 0; | |
189 | bufptr = grub_stpcpy (buf, alias->path); | |
190 | ||
191 | for (i = 0; i < args.nentries; i++) | |
192 | { | |
193 | grub_uint64_t *ptr; | |
194 | ||
195 | ptr = *(grub_uint64_t **) (args.table + 4 + 8 * i); | |
196 | while (*ptr) | |
197 | { | |
198 | grub_snprintf (bufptr, 32, "/disk@%" PRIxGRUB_UINT64_T, *ptr++); | |
199 | if (dev_iterate_real (buf, buf)) | |
200 | return 1; | |
201 | } | |
202 | } | |
203 | grub_ieee1275_close (ihandle); | |
204 | grub_free (buf); | |
205 | return 0; | |
206 | } | |
207 | ||
208 | if (!grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS) | |
209 | && grub_strcmp (alias->type, "block") == 0) | |
210 | return dev_iterate_real (alias->path, alias->path); | |
211 | ||
212 | return grub_children_iterate (alias->path, dev_iterate); | |
0ec82090 VS |
213 | } |
214 | ||
215 | grub_devalias_iterate (dev_iterate_alias); | |
bf293dec | 216 | grub_children_iterate ("/", dev_iterate); |
b2a30ac5 | 217 | } |
25638629 | 218 | |
b2a30ac5 | 219 | static int |
00542307 VS |
220 | grub_ofdisk_iterate (int (*hook) (const char *name), |
221 | grub_disk_pull_t pull) | |
b2a30ac5 VS |
222 | { |
223 | unsigned i; | |
00542307 VS |
224 | |
225 | if (pull != GRUB_DISK_PULL_NONE) | |
226 | return 0; | |
227 | ||
b2a30ac5 VS |
228 | scan (); |
229 | ||
230 | for (i = 0; i < ARRAY_SIZE (ofdisk_hash); i++) | |
231 | { | |
232 | static struct ofdisk_hash_ent *ent; | |
233 | for (ent = ofdisk_hash[i]; ent; ent = ent->next) | |
234 | { | |
235 | if (!ent->shortest) | |
236 | continue; | |
237 | if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_OFDISK_SDCARD_ONLY)) | |
25638629 | 238 | { |
b2a30ac5 VS |
239 | grub_ieee1275_phandle_t dev; |
240 | char tmp[8]; | |
241 | ||
242 | if (grub_ieee1275_finddevice (ent->devpath, &dev)) | |
243 | { | |
244 | grub_dprintf ("disk", "finddevice (%s) failed\n", | |
245 | ent->devpath); | |
246 | continue; | |
247 | } | |
248 | ||
249 | if (grub_ieee1275_get_property (dev, "iconname", tmp, | |
250 | sizeof tmp, 0)) | |
251 | { | |
252 | grub_dprintf ("disk", "get iconname failed\n"); | |
253 | continue; | |
254 | } | |
255 | ||
256 | if (grub_strcmp (tmp, "sdmmc") != 0) | |
257 | { | |
258 | grub_dprintf ("disk", "device is not an SD card\n"); | |
259 | continue; | |
260 | } | |
25638629 | 261 | } |
25638629 | 262 | |
b2a30ac5 VS |
263 | if (grub_strncmp (ent->shortest, "cdrom", 5) == 0) |
264 | continue; | |
e56cdf21 | 265 | |
ca74c50c | 266 | { |
9197b0ad | 267 | char buffer[sizeof ("ieee1275/") + grub_strlen (ent->shortest)]; |
ca74c50c VS |
268 | char *ptr; |
269 | ptr = grub_stpcpy (buffer, "ieee1275/"); | |
9197b0ad | 270 | grub_strcpy (ptr, ent->shortest); |
ca74c50c VS |
271 | if (hook (buffer)) |
272 | return 1; | |
273 | } | |
b2a30ac5 VS |
274 | } |
275 | } | |
276 | return 0; | |
e56cdf21 | 277 | } |
278 | ||
67e23c90 | 279 | static char * |
280 | compute_dev_path (const char *name) | |
281 | { | |
3b205d4d | 282 | char *devpath = grub_malloc (grub_strlen (name) + 3); |
67e23c90 | 283 | char *p, c; |
284 | ||
285 | if (!devpath) | |
286 | return NULL; | |
287 | ||
288 | /* Un-escape commas. */ | |
289 | p = devpath; | |
290 | while ((c = *name++) != '\0') | |
291 | { | |
292 | if (c == '\\' && *name == ',') | |
293 | { | |
294 | *p++ = ','; | |
295 | name++; | |
296 | } | |
297 | else | |
298 | *p++ = c; | |
299 | } | |
300 | ||
67e23c90 | 301 | *p++ = '\0'; |
302 | ||
303 | return devpath; | |
304 | } | |
305 | ||
4b13b216 | 306 | static grub_err_t |
0044d1db | 307 | grub_ofdisk_open (const char *name, grub_disk_t disk) |
e56cdf21 | 308 | { |
4b13b216 | 309 | grub_ieee1275_phandle_t dev; |
8b8cbdb2 | 310 | char *devpath; |
e56cdf21 | 311 | /* XXX: This should be large enough for any possible case. */ |
312 | char prop[64]; | |
e9211b5d | 313 | grub_ssize_t actual; |
e56cdf21 | 314 | |
9197b0ad | 315 | if (grub_strncmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) != 0) |
ca74c50c VS |
316 | return grub_error (GRUB_ERR_UNKNOWN_DEVICE, |
317 | "not IEEE1275 device"); | |
318 | devpath = compute_dev_path (name + sizeof ("ieee1275/") - 1); | |
e56cdf21 | 319 | if (! devpath) |
4b13b216 | 320 | return grub_errno; |
e56cdf21 | 321 | |
b2a30ac5 | 322 | grub_dprintf ("disk", "Opening `%s'.\n", devpath); |
09f9923f | 323 | |
b2a30ac5 | 324 | if (grub_ieee1275_finddevice (devpath, &dev)) |
c97dbbf2 VS |
325 | { |
326 | grub_free (devpath); | |
327 | return grub_error (GRUB_ERR_UNKNOWN_DEVICE, | |
328 | "can't read device properties"); | |
329 | } | |
e56cdf21 | 330 | |
4b13b216 | 331 | if (grub_ieee1275_get_property (dev, "device_type", prop, sizeof (prop), |
e56cdf21 | 332 | &actual)) |
c97dbbf2 VS |
333 | { |
334 | grub_free (devpath); | |
335 | return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't read the device type"); | |
336 | } | |
e56cdf21 | 337 | |
4b13b216 | 338 | if (grub_strcmp (prop, "block")) |
c97dbbf2 VS |
339 | { |
340 | grub_free (devpath); | |
9c4b5c13 | 341 | return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a block device"); |
c97dbbf2 | 342 | } |
5b59a4e3 | 343 | |
e56cdf21 | 344 | /* XXX: There is no property to read the number of blocks. There |
345 | should be a property `#blocks', but it is not there. Perhaps it | |
346 | is possible to use seek for this. */ | |
b1d17e10 | 347 | disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN; |
e56cdf21 | 348 | |
b2a30ac5 VS |
349 | { |
350 | struct ofdisk_hash_ent *op; | |
351 | op = ofdisk_hash_find (devpath); | |
352 | if (!op) | |
b44a558c | 353 | op = ofdisk_hash_add (devpath, NULL); |
b2a30ac5 VS |
354 | else |
355 | grub_free (devpath); | |
356 | if (!op) | |
357 | return grub_errno; | |
358 | disk->id = (unsigned long) op; | |
359 | disk->data = op->devpath; | |
360 | } | |
e56cdf21 | 361 | |
44cb1ec8 | 362 | return 0; |
e56cdf21 | 363 | } |
364 | ||
365 | static void | |
4b13b216 | 366 | grub_ofdisk_close (grub_disk_t disk) |
e56cdf21 | 367 | { |
b2a30ac5 VS |
368 | if (disk->data == last_devpath) |
369 | { | |
370 | if (last_ihandle) | |
371 | grub_ieee1275_close (last_ihandle); | |
372 | last_ihandle = 0; | |
373 | last_devpath = NULL; | |
374 | } | |
375 | disk->data = 0; | |
e56cdf21 | 376 | } |
377 | ||
4b13b216 | 378 | static grub_err_t |
76261110 | 379 | grub_ofdisk_prepare (grub_disk_t disk, grub_disk_addr_t sector) |
e56cdf21 | 380 | { |
76261110 | 381 | grub_ssize_t status; |
e56cdf21 | 382 | unsigned long long pos; |
383 | ||
b2a30ac5 VS |
384 | if (disk->data != last_devpath) |
385 | { | |
386 | if (last_ihandle) | |
387 | grub_ieee1275_close (last_ihandle); | |
388 | last_ihandle = 0; | |
389 | last_devpath = NULL; | |
390 | ||
391 | if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PARTITION_0)) | |
392 | { | |
393 | char name2[grub_strlen (disk->data) + 3]; | |
394 | char *p; | |
395 | ||
396 | grub_strcpy (name2, disk->data); | |
397 | p = name2 + grub_strlen (name2); | |
398 | *p++ = ':'; | |
399 | *p++ = '0'; | |
400 | *p = 0; | |
401 | grub_ieee1275_open (name2, &last_ihandle); | |
402 | } | |
403 | else | |
404 | grub_ieee1275_open (disk->data, &last_ihandle); | |
405 | if (! last_ihandle) | |
406 | return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); | |
407 | last_devpath = disk->data; | |
408 | } | |
409 | ||
76261110 | 410 | pos = sector << GRUB_DISK_SECTOR_BITS; |
e56cdf21 | 411 | |
b2a30ac5 | 412 | grub_ieee1275_seek (last_ihandle, pos, &status); |
e2b8278c | 413 | if (status < 0) |
4b13b216 | 414 | return grub_error (GRUB_ERR_READ_ERROR, |
7fd0baee | 415 | "seek error, can't seek block %llu", |
979b4fb4 | 416 | (long long) sector); |
76261110 VS |
417 | return 0; |
418 | } | |
419 | ||
420 | static grub_err_t | |
421 | grub_ofdisk_read (grub_disk_t disk, grub_disk_addr_t sector, | |
422 | grub_size_t size, char *buf) | |
423 | { | |
424 | grub_err_t err; | |
425 | grub_ssize_t actual; | |
426 | err = grub_ofdisk_prepare (disk, sector); | |
427 | if (err) | |
428 | return err; | |
429 | grub_ieee1275_read (last_ihandle, buf, size << GRUB_DISK_SECTOR_BITS, | |
430 | &actual); | |
431 | if (actual != (grub_ssize_t) (size << GRUB_DISK_SECTOR_BITS)) | |
9c4b5c13 | 432 | return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx " |
d61386e2 | 433 | "from `%s'"), |
9c4b5c13 VS |
434 | (unsigned long long) sector, |
435 | disk->name); | |
b39f9d20 | 436 | |
e56cdf21 | 437 | return 0; |
438 | } | |
439 | ||
4b13b216 | 440 | static grub_err_t |
76261110 VS |
441 | grub_ofdisk_write (grub_disk_t disk, grub_disk_addr_t sector, |
442 | grub_size_t size, const char *buf) | |
e56cdf21 | 443 | { |
76261110 VS |
444 | grub_err_t err; |
445 | grub_ssize_t actual; | |
446 | err = grub_ofdisk_prepare (disk, sector); | |
447 | if (err) | |
448 | return err; | |
449 | grub_ieee1275_write (last_ihandle, buf, size << GRUB_DISK_SECTOR_BITS, | |
450 | &actual); | |
451 | if (actual != (grub_ssize_t) (size << GRUB_DISK_SECTOR_BITS)) | |
9c4b5c13 | 452 | return grub_error (GRUB_ERR_WRITE_ERROR, N_("failure writing sector 0x%llx " |
d61386e2 | 453 | "to `%s'"), |
9c4b5c13 VS |
454 | (unsigned long long) sector, |
455 | disk->name); | |
76261110 VS |
456 | |
457 | return 0; | |
e56cdf21 | 458 | } |
459 | ||
4b13b216 | 460 | static struct grub_disk_dev grub_ofdisk_dev = |
e56cdf21 | 461 | { |
462 | .name = "ofdisk", | |
97543f08 | 463 | .id = GRUB_DISK_DEVICE_OFDISK_ID, |
4b13b216 | 464 | .iterate = grub_ofdisk_iterate, |
465 | .open = grub_ofdisk_open, | |
466 | .close = grub_ofdisk_close, | |
467 | .read = grub_ofdisk_read, | |
468 | .write = grub_ofdisk_write, | |
e56cdf21 | 469 | .next = 0 |
470 | }; | |
471 | ||
472 | void | |
4b13b216 | 473 | grub_ofdisk_init (void) |
e56cdf21 | 474 | { |
4b13b216 | 475 | grub_disk_dev_register (&grub_ofdisk_dev); |
e56cdf21 | 476 | } |
a5ce3a4a | 477 | |
478 | void | |
479 | grub_ofdisk_fini (void) | |
480 | { | |
b2a30ac5 VS |
481 | if (last_ihandle) |
482 | grub_ieee1275_close (last_ihandle); | |
483 | last_ihandle = 0; | |
484 | last_devpath = NULL; | |
485 | ||
a5ce3a4a | 486 | grub_disk_dev_unregister (&grub_ofdisk_dev); |
487 | } |