Fix cross compilation (e.g. on Darwin). Following changes to make.tmpl,
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / kern / disk.c
blob789f8c05233168f74dab6b23815bac6f9ffcd7da
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2002,2003,2004,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/disk.h>
20 #include <grub/err.h>
21 #include <grub/mm.h>
22 #include <grub/types.h>
23 #include <grub/partition.h>
24 #include <grub/misc.h>
25 #include <grub/time.h>
26 #include <grub/file.h>
27 #include <grub/i18n.h>
29 #define GRUB_CACHE_TIMEOUT 2
31 /* The last time the disk was used. */
32 static grub_uint64_t grub_last_time = 0;
34 struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
36 void (*grub_disk_firmware_fini) (void);
37 int grub_disk_firmware_is_tainted;
39 #if DISK_CACHE_STATS
40 static unsigned long grub_disk_cache_hits;
41 static unsigned long grub_disk_cache_misses;
43 void
44 grub_disk_cache_get_performance (unsigned long *hits, unsigned long *misses)
46 *hits = grub_disk_cache_hits;
47 *misses = grub_disk_cache_misses;
49 #endif
51 grub_err_t (*grub_disk_write_weak) (grub_disk_t disk,
52 grub_disk_addr_t sector,
53 grub_off_t offset,
54 grub_size_t size,
55 const void *buf);
56 #include "disk_common.c"
58 void
59 grub_disk_cache_invalidate_all (void)
61 unsigned i;
63 for (i = 0; i < GRUB_DISK_CACHE_NUM; i++)
65 struct grub_disk_cache *cache = grub_disk_cache_table + i;
67 if (cache->data && ! cache->lock)
69 grub_free (cache->data);
70 cache->data = 0;
75 static char *
76 grub_disk_cache_fetch (unsigned long dev_id, unsigned long disk_id,
77 grub_disk_addr_t sector)
79 struct grub_disk_cache *cache;
80 unsigned cache_index;
82 cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
83 cache = grub_disk_cache_table + cache_index;
85 if (cache->dev_id == dev_id && cache->disk_id == disk_id
86 && cache->sector == sector)
88 cache->lock = 1;
89 #if DISK_CACHE_STATS
90 grub_disk_cache_hits++;
91 #endif
92 return cache->data;
95 #if DISK_CACHE_STATS
96 grub_disk_cache_misses++;
97 #endif
99 return 0;
102 static void
103 grub_disk_cache_unlock (unsigned long dev_id, unsigned long disk_id,
104 grub_disk_addr_t sector)
106 struct grub_disk_cache *cache;
107 unsigned cache_index;
109 cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
110 cache = grub_disk_cache_table + cache_index;
112 if (cache->dev_id == dev_id && cache->disk_id == disk_id
113 && cache->sector == sector)
114 cache->lock = 0;
117 static grub_err_t
118 grub_disk_cache_store (unsigned long dev_id, unsigned long disk_id,
119 grub_disk_addr_t sector, const char *data)
121 unsigned cache_index;
122 struct grub_disk_cache *cache;
124 cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
125 cache = grub_disk_cache_table + cache_index;
127 cache->lock = 1;
128 grub_free (cache->data);
129 cache->data = 0;
130 cache->lock = 0;
132 cache->data = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
133 if (! cache->data)
134 return grub_errno;
136 grub_memcpy (cache->data, data,
137 GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
138 cache->dev_id = dev_id;
139 cache->disk_id = disk_id;
140 cache->sector = sector;
142 return GRUB_ERR_NONE;
147 grub_disk_dev_t grub_disk_dev_list;
149 void
150 grub_disk_dev_register (grub_disk_dev_t dev)
152 dev->next = grub_disk_dev_list;
153 grub_disk_dev_list = dev;
156 void
157 grub_disk_dev_unregister (grub_disk_dev_t dev)
159 grub_disk_dev_t *p, q;
161 for (p = &grub_disk_dev_list, q = *p; q; p = &(q->next), q = q->next)
162 if (q == dev)
164 *p = q->next;
165 break;
169 /* Return the location of the first ',', if any, which is not
170 escaped by a '\'. */
171 static const char *
172 find_part_sep (const char *name)
174 const char *p = name;
175 char c;
177 while ((c = *p++) != '\0')
179 if (c == '\\' && *p == ',')
180 p++;
181 else if (c == ',')
182 return p - 1;
184 return NULL;
187 grub_disk_t
188 grub_disk_open (const char *name)
190 const char *p;
191 grub_disk_t disk;
192 grub_disk_dev_t dev;
193 char *raw = (char *) name;
194 grub_uint64_t current_time;
196 grub_dprintf ("disk", "Opening `%s'...\n", name);
198 disk = (grub_disk_t) grub_zalloc (sizeof (*disk));
199 if (! disk)
200 return 0;
201 disk->log_sector_size = GRUB_DISK_SECTOR_BITS;
202 /* Default 1MiB of maximum agglomerate. */
203 disk->max_agglomerate = 1048576 >> (GRUB_DISK_SECTOR_BITS
204 + GRUB_DISK_CACHE_BITS);
206 p = find_part_sep (name);
207 if (p)
209 grub_size_t len = p - name;
211 raw = grub_malloc (len + 1);
212 if (! raw)
213 goto fail;
215 grub_memcpy (raw, name, len);
216 raw[len] = '\0';
217 disk->name = grub_strdup (raw);
219 else
220 disk->name = grub_strdup (name);
221 if (! disk->name)
222 goto fail;
224 for (dev = grub_disk_dev_list; dev; dev = dev->next)
226 if ((dev->open) (raw, disk) == GRUB_ERR_NONE)
227 break;
228 else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
229 grub_errno = GRUB_ERR_NONE;
230 else
231 goto fail;
234 if (! dev)
236 grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"),
237 name);
238 goto fail;
240 if (disk->log_sector_size > GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS
241 || disk->log_sector_size < GRUB_DISK_SECTOR_BITS)
243 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
244 "sector sizes of %d bytes aren't supported yet",
245 (1 << disk->log_sector_size));
246 goto fail;
249 disk->dev = dev;
251 if (p)
253 disk->partition = grub_partition_probe (disk, p + 1);
254 if (! disk->partition)
256 /* TRANSLATORS: It means that the specified partition e.g.
257 hd0,msdos1=/dev/sda1 doesn't exist. */
258 grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such partition"));
259 goto fail;
263 /* The cache will be invalidated about 2 seconds after a device was
264 closed. */
265 current_time = grub_get_time_ms ();
267 if (current_time > (grub_last_time
268 + GRUB_CACHE_TIMEOUT * 1000))
269 grub_disk_cache_invalidate_all ();
271 grub_last_time = current_time;
273 fail:
275 if (raw && raw != name)
276 grub_free (raw);
278 if (grub_errno != GRUB_ERR_NONE)
280 grub_error_push ();
281 grub_dprintf ("disk", "Opening `%s' failed.\n", name);
282 grub_error_pop ();
284 grub_disk_close (disk);
285 return 0;
288 return disk;
291 void
292 grub_disk_close (grub_disk_t disk)
294 grub_partition_t part;
295 grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
297 if (disk->dev && disk->dev->close)
298 (disk->dev->close) (disk);
300 /* Reset the timer. */
301 grub_last_time = grub_get_time_ms ();
303 while (disk->partition)
305 part = disk->partition->parent;
306 grub_free (disk->partition);
307 disk->partition = part;
309 grub_free ((void *) disk->name);
310 grub_free (disk);
313 /* Small read (less than cache size and not pass across cache unit boundaries).
314 sector is already adjusted and is divisible by cache unit size.
316 static grub_err_t
317 grub_disk_read_small_real (grub_disk_t disk, grub_disk_addr_t sector,
318 grub_off_t offset, grub_size_t size, void *buf)
320 char *data;
321 char *tmp_buf;
323 /* Fetch the cache. */
324 data = grub_disk_cache_fetch (disk->dev->id, disk->id, sector);
325 if (data)
327 /* Just copy it! */
328 grub_memcpy (buf, data + offset, size);
329 grub_disk_cache_unlock (disk->dev->id, disk->id, sector);
330 return GRUB_ERR_NONE;
333 /* Allocate a temporary buffer. */
334 tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
335 if (! tmp_buf)
336 return grub_errno;
338 /* Otherwise read data from the disk actually. */
339 if (disk->total_sectors == GRUB_DISK_SIZE_UNKNOWN
340 || sector + GRUB_DISK_CACHE_SIZE
341 < (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
343 grub_err_t err;
344 err = (disk->dev->read) (disk, transform_sector (disk, sector),
345 1U << (GRUB_DISK_CACHE_BITS
346 + GRUB_DISK_SECTOR_BITS
347 - disk->log_sector_size), tmp_buf);
348 if (!err)
350 /* Copy it and store it in the disk cache. */
351 grub_memcpy (buf, tmp_buf + offset, size);
352 grub_disk_cache_store (disk->dev->id, disk->id,
353 sector, tmp_buf);
354 grub_free (tmp_buf);
355 return GRUB_ERR_NONE;
359 grub_free (tmp_buf);
360 grub_errno = GRUB_ERR_NONE;
363 /* Uggh... Failed. Instead, just read necessary data. */
364 unsigned num;
365 grub_disk_addr_t aligned_sector;
367 sector += (offset >> GRUB_DISK_SECTOR_BITS);
368 offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
369 aligned_sector = (sector & ~((1ULL << (disk->log_sector_size
370 - GRUB_DISK_SECTOR_BITS))
371 - 1));
372 offset += ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS);
373 num = ((size + offset + (1ULL << (disk->log_sector_size))
374 - 1) >> (disk->log_sector_size));
376 tmp_buf = grub_malloc (num << disk->log_sector_size);
377 if (!tmp_buf)
378 return grub_errno;
380 if ((disk->dev->read) (disk, transform_sector (disk, aligned_sector),
381 num, tmp_buf))
383 grub_error_push ();
384 grub_dprintf ("disk", "%s read failed\n", disk->name);
385 grub_error_pop ();
386 grub_free (tmp_buf);
387 return grub_errno;
389 grub_memcpy (buf, tmp_buf + offset, size);
390 grub_free (tmp_buf);
391 return GRUB_ERR_NONE;
395 static grub_err_t
396 grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
397 grub_off_t offset, grub_size_t size, void *buf)
399 grub_err_t err;
401 err = grub_disk_read_small_real (disk, sector, offset, size, buf);
402 if (err)
403 return err;
404 if (disk->read_hook)
405 (disk->read_hook) (sector + (offset >> GRUB_DISK_SECTOR_BITS),
406 offset & (GRUB_DISK_SECTOR_SIZE - 1),
407 size, disk->read_hook_data);
408 return GRUB_ERR_NONE;
411 /* Read data from the disk. */
412 grub_err_t
413 grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
414 grub_off_t offset, grub_size_t size, void *buf)
416 /* First of all, check if the region is within the disk. */
417 if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
419 grub_error_push ();
420 grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n",
421 (unsigned long long) sector, grub_errmsg);
422 grub_error_pop ();
423 return grub_errno;
426 /* First read until first cache boundary. */
427 if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1)))
429 grub_disk_addr_t start_sector;
430 grub_size_t pos;
431 grub_err_t err;
432 grub_size_t len;
434 start_sector = sector & ~((grub_disk_addr_t) GRUB_DISK_CACHE_SIZE - 1);
435 pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
436 len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
437 - pos - offset);
438 if (len > size)
439 len = size;
440 err = grub_disk_read_small (disk, start_sector,
441 offset + pos, len, buf);
442 if (err)
443 return err;
444 buf = (char *) buf + len;
445 size -= len;
446 offset += len;
447 sector += (offset >> GRUB_DISK_SECTOR_BITS);
448 offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
451 /* Until SIZE is zero... */
452 while (size >= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS))
454 char *data = NULL;
455 grub_disk_addr_t agglomerate;
456 grub_err_t err;
458 /* agglomerate read until we find a first cached entry. */
459 for (agglomerate = 0; agglomerate
460 < (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS))
461 && agglomerate < disk->max_agglomerate;
462 agglomerate++)
464 data = grub_disk_cache_fetch (disk->dev->id, disk->id,
465 sector + (agglomerate
466 << GRUB_DISK_CACHE_BITS));
467 if (data)
468 break;
471 if (data)
473 grub_memcpy ((char *) buf
474 + (agglomerate << (GRUB_DISK_CACHE_BITS
475 + GRUB_DISK_SECTOR_BITS)),
476 data, GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
477 grub_disk_cache_unlock (disk->dev->id, disk->id,
478 sector + (agglomerate
479 << GRUB_DISK_CACHE_BITS));
482 if (agglomerate)
484 grub_disk_addr_t i;
486 err = (disk->dev->read) (disk, transform_sector (disk, sector),
487 agglomerate << (GRUB_DISK_CACHE_BITS
488 + GRUB_DISK_SECTOR_BITS
489 - disk->log_sector_size),
490 buf);
491 if (err)
492 return err;
494 for (i = 0; i < agglomerate; i ++)
495 grub_disk_cache_store (disk->dev->id, disk->id,
496 sector + (i << GRUB_DISK_CACHE_BITS),
497 (char *) buf
498 + (i << (GRUB_DISK_CACHE_BITS
499 + GRUB_DISK_SECTOR_BITS)));
502 if (disk->read_hook)
503 (disk->read_hook) (sector, 0, agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS),
504 disk->read_hook_data);
506 sector += agglomerate << GRUB_DISK_CACHE_BITS;
507 size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
508 buf = (char *) buf
509 + (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
512 if (data)
514 if (disk->read_hook)
515 (disk->read_hook) (sector, 0, (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS),
516 disk->read_hook_data);
517 sector += GRUB_DISK_CACHE_SIZE;
518 buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
519 size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
523 /* And now read the last part. */
524 if (size)
526 grub_err_t err;
527 err = grub_disk_read_small (disk, sector, 0, size, buf);
528 if (err)
529 return err;
532 return grub_errno;
535 grub_uint64_t
536 grub_disk_get_size (grub_disk_t disk)
538 if (disk->partition)
539 return grub_partition_get_len (disk->partition);
540 else if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN)
541 return disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
542 else
543 return GRUB_DISK_SIZE_UNKNOWN;