]> git.proxmox.com Git - grub2.git/blame - grub-core/disk/ieee1275/ofdisk.c
* grub-core/disk/ieee1275/ofdisk.c (scan): Support vscsi on IBM
[grub2.git] / grub-core / disk / ieee1275 / ofdisk.c
CommitLineData
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
27static char *last_devpath;
28static grub_ieee1275_ihandle_t last_ihandle;
29
f5dbbca9 30struct 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
40static struct ofdisk_hash_ent *ofdisk_hash[OFDISK_HASH_SZ];
41
42static int
43ofdisk_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
51static struct ofdisk_hash_ent *
52ofdisk_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
65static struct ofdisk_hash_ent *
b44a558c 66ofdisk_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
82static struct ofdisk_hash_ent *
83ofdisk_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
115static void
116scan (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 219static int
00542307
VS
220grub_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 279static char *
280compute_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 306static grub_err_t
0044d1db 307grub_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
365static void
4b13b216 366grub_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 378static grub_err_t
76261110 379grub_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
420static grub_err_t
421grub_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 440static grub_err_t
76261110
VS
441grub_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 460static 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
472void
4b13b216 473grub_ofdisk_init (void)
e56cdf21 474{
4b13b216 475 grub_disk_dev_register (&grub_ofdisk_dev);
e56cdf21 476}
a5ce3a4a 477
478void
479grub_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}