]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/grub-2.04/grub-core/kern/disk.c
Update PhyDrive.c
[Ventoy.git] / GRUB2 / grub-2.04 / grub-core / kern / disk.c
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2002,2003,2004,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
4 *
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.
9 *
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.
14 *
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/>.
17 */
18
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>
28 #include <grub/ventoy.h>
29
30 #define GRUB_CACHE_TIMEOUT 2
31
32 /* The last time the disk was used. */
33 static grub_uint64_t grub_last_time = 0;
34
35 struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
36
37 void (*grub_disk_firmware_fini) (void);
38 int grub_disk_firmware_is_tainted;
39
40 #if DISK_CACHE_STATS
41 static unsigned long grub_disk_cache_hits;
42 static unsigned long grub_disk_cache_misses;
43
44 void
45 grub_disk_cache_get_performance (unsigned long *hits, unsigned long *misses)
46 {
47 *hits = grub_disk_cache_hits;
48 *misses = grub_disk_cache_misses;
49 }
50 #endif
51
52 grub_err_t (*grub_disk_write_weak) (grub_disk_t disk,
53 grub_disk_addr_t sector,
54 grub_off_t offset,
55 grub_size_t size,
56 const void *buf);
57 #include "disk_common.c"
58
59 void
60 grub_disk_cache_invalidate_all (void)
61 {
62 unsigned i;
63
64 for (i = 0; i < GRUB_DISK_CACHE_NUM; i++)
65 {
66 struct grub_disk_cache *cache = grub_disk_cache_table + i;
67
68 if (cache->data && ! cache->lock)
69 {
70 grub_free (cache->data);
71 cache->data = 0;
72 }
73 }
74 }
75
76 static char *
77 grub_disk_cache_fetch (unsigned long dev_id, unsigned long disk_id,
78 grub_disk_addr_t sector)
79 {
80 struct grub_disk_cache *cache;
81 unsigned cache_index;
82
83 cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
84 cache = grub_disk_cache_table + cache_index;
85
86 if (cache->dev_id == dev_id && cache->disk_id == disk_id
87 && cache->sector == sector)
88 {
89 cache->lock = 1;
90 #if DISK_CACHE_STATS
91 grub_disk_cache_hits++;
92 #endif
93 return cache->data;
94 }
95
96 #if DISK_CACHE_STATS
97 grub_disk_cache_misses++;
98 #endif
99
100 return 0;
101 }
102
103 static void
104 grub_disk_cache_unlock (unsigned long dev_id, unsigned long disk_id,
105 grub_disk_addr_t sector)
106 {
107 struct grub_disk_cache *cache;
108 unsigned cache_index;
109
110 cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
111 cache = grub_disk_cache_table + cache_index;
112
113 if (cache->dev_id == dev_id && cache->disk_id == disk_id
114 && cache->sector == sector)
115 cache->lock = 0;
116 }
117
118 static grub_err_t
119 grub_disk_cache_store (unsigned long dev_id, unsigned long disk_id,
120 grub_disk_addr_t sector, const char *data)
121 {
122 unsigned cache_index;
123 struct grub_disk_cache *cache;
124
125 cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
126 cache = grub_disk_cache_table + cache_index;
127
128 cache->lock = 1;
129 grub_free (cache->data);
130 cache->data = 0;
131 cache->lock = 0;
132
133 cache->data = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
134 if (! cache->data)
135 return grub_errno;
136
137 grub_memcpy (cache->data, data,
138 GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
139 cache->dev_id = dev_id;
140 cache->disk_id = disk_id;
141 cache->sector = sector;
142
143 return GRUB_ERR_NONE;
144 }
145
146 \f
147
148 grub_disk_dev_t grub_disk_dev_list;
149
150 void
151 grub_disk_dev_register (grub_disk_dev_t dev)
152 {
153 dev->next = grub_disk_dev_list;
154 grub_disk_dev_list = dev;
155 }
156
157 void
158 grub_disk_dev_unregister (grub_disk_dev_t dev)
159 {
160 grub_disk_dev_t *p, q;
161
162 for (p = &grub_disk_dev_list, q = *p; q; p = &(q->next), q = q->next)
163 if (q == dev)
164 {
165 *p = q->next;
166 break;
167 }
168 }
169
170 /* Return the location of the first ',', if any, which is not
171 escaped by a '\'. */
172 static const char *
173 find_part_sep (const char *name)
174 {
175 const char *p = name;
176 char c;
177
178 while ((c = *p++) != '\0')
179 {
180 if (c == '\\' && *p == ',')
181 p++;
182 else if (c == ',')
183 return p - 1;
184 }
185 return NULL;
186 }
187
188 grub_disk_t
189 grub_disk_open (const char *name)
190 {
191 const char *p;
192 grub_disk_t disk;
193 grub_disk_dev_t dev;
194 char *raw = (char *) name;
195 grub_uint64_t current_time;
196
197 grub_dprintf ("disk", "Opening `%s'...\n", name);
198
199 disk = (grub_disk_t) grub_zalloc (sizeof (*disk));
200 if (! disk)
201 return 0;
202 disk->log_sector_size = GRUB_DISK_SECTOR_BITS;
203 /* Default 1MiB of maximum agglomerate. */
204 disk->max_agglomerate = 1048576 >> (GRUB_DISK_SECTOR_BITS
205 + GRUB_DISK_CACHE_BITS);
206
207 p = find_part_sep (name);
208 if (p)
209 {
210 grub_size_t len = p - name;
211
212 raw = grub_malloc (len + 1);
213 if (! raw)
214 goto fail;
215
216 grub_memcpy (raw, name, len);
217 raw[len] = '\0';
218 disk->name = grub_strdup (raw);
219 }
220 else
221 disk->name = grub_strdup (name);
222 if (! disk->name)
223 goto fail;
224
225 for (dev = grub_disk_dev_list; dev; dev = dev->next)
226 {
227 if ((dev->disk_open) (raw, disk) == GRUB_ERR_NONE)
228 break;
229 else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
230 grub_errno = GRUB_ERR_NONE;
231 else
232 goto fail;
233 }
234
235 if (! dev)
236 {
237 grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"),
238 name);
239 goto fail;
240 }
241 if (disk->log_sector_size > GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS
242 || disk->log_sector_size < GRUB_DISK_SECTOR_BITS)
243 {
244 grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
245 "sector sizes of %d bytes aren't supported yet",
246 (1 << disk->log_sector_size));
247 goto fail;
248 }
249
250 disk->dev = dev;
251
252 if (p)
253 {
254 disk->partition = grub_partition_probe (disk, p + 1);
255 if (! disk->partition)
256 {
257 /* TRANSLATORS: It means that the specified partition e.g.
258 hd0,msdos1=/dev/sda1 doesn't exist. */
259 grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such partition"));
260 goto fail;
261 }
262 }
263
264 /* The cache will be invalidated about 2 seconds after a device was
265 closed. */
266 current_time = grub_get_time_ms ();
267
268 if (current_time > (grub_last_time
269 + GRUB_CACHE_TIMEOUT * 1000))
270 grub_disk_cache_invalidate_all ();
271
272 grub_last_time = current_time;
273
274 fail:
275
276 if (raw && raw != name)
277 grub_free (raw);
278
279 if (grub_errno != GRUB_ERR_NONE)
280 {
281 grub_error_push ();
282 grub_dprintf ("disk", "Opening `%s' failed.\n", name);
283 grub_error_pop ();
284
285 grub_disk_close (disk);
286 return 0;
287 }
288
289 return disk;
290 }
291
292 void
293 grub_disk_close (grub_disk_t disk)
294 {
295 grub_partition_t part;
296 grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
297
298 if (disk->dev && disk->dev->disk_close)
299 (disk->dev->disk_close) (disk);
300
301 /* Reset the timer. */
302 grub_last_time = grub_get_time_ms ();
303
304 while (disk->partition)
305 {
306 part = disk->partition->parent;
307 grub_free (disk->partition);
308 disk->partition = part;
309 }
310 grub_free ((void *) disk->name);
311 grub_free (disk);
312 }
313
314 /* Small read (less than cache size and not pass across cache unit boundaries).
315 sector is already adjusted and is divisible by cache unit size.
316 */
317 static grub_err_t
318 grub_disk_read_small_real (grub_disk_t disk, grub_disk_addr_t sector,
319 grub_off_t offset, grub_size_t size, void *buf)
320 {
321 char *data;
322 char *tmp_buf;
323
324 /* Fetch the cache. */
325 data = grub_disk_cache_fetch (disk->dev->id, disk->id, sector);
326 if (data)
327 {
328 /* Just copy it! */
329 grub_memcpy (buf, data + offset, size);
330 grub_disk_cache_unlock (disk->dev->id, disk->id, sector);
331 return GRUB_ERR_NONE;
332 }
333
334 /* Allocate a temporary buffer. */
335 tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
336 if (! tmp_buf)
337 return grub_errno;
338
339 /* Otherwise read data from the disk actually. */
340 if (disk->total_sectors == GRUB_DISK_SIZE_UNKNOWN
341 || sector + GRUB_DISK_CACHE_SIZE
342 < (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
343 {
344 grub_err_t err;
345 err = (disk->dev->disk_read) (disk, transform_sector (disk, sector),
346 1U << (GRUB_DISK_CACHE_BITS
347 + GRUB_DISK_SECTOR_BITS
348 - disk->log_sector_size), tmp_buf);
349 if (!err)
350 {
351 /* Copy it and store it in the disk cache. */
352 grub_memcpy (buf, tmp_buf + offset, size);
353 grub_disk_cache_store (disk->dev->id, disk->id,
354 sector, tmp_buf);
355 grub_free (tmp_buf);
356 return GRUB_ERR_NONE;
357 }
358 }
359
360 grub_free (tmp_buf);
361 grub_errno = GRUB_ERR_NONE;
362
363 {
364 /* Uggh... Failed. Instead, just read necessary data. */
365 unsigned num;
366 grub_disk_addr_t aligned_sector;
367
368 sector += (offset >> GRUB_DISK_SECTOR_BITS);
369 offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
370 aligned_sector = (sector & ~((1ULL << (disk->log_sector_size
371 - GRUB_DISK_SECTOR_BITS))
372 - 1));
373 offset += ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS);
374 num = ((size + offset + (1ULL << (disk->log_sector_size))
375 - 1) >> (disk->log_sector_size));
376
377 tmp_buf = grub_malloc (num << disk->log_sector_size);
378 if (!tmp_buf)
379 return grub_errno;
380
381 if ((disk->dev->disk_read) (disk, transform_sector (disk, aligned_sector),
382 num, tmp_buf))
383 {
384 grub_error_push ();
385 grub_dprintf ("disk", "%s read failed\n", disk->name);
386 grub_error_pop ();
387 grub_free (tmp_buf);
388 return grub_errno;
389 }
390 grub_memcpy (buf, tmp_buf + offset, size);
391 grub_free (tmp_buf);
392 return GRUB_ERR_NONE;
393 }
394 }
395
396 static grub_err_t
397 grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
398 grub_off_t offset, grub_size_t size, void *buf)
399 {
400 grub_err_t err;
401
402 err = grub_disk_read_small_real (disk, sector, offset, size, buf);
403 if (err)
404 return err;
405 if (disk->read_hook)
406 (disk->read_hook) (sector + (offset >> GRUB_DISK_SECTOR_BITS),
407 offset & (GRUB_DISK_SECTOR_SIZE - 1),
408 size, disk->read_hook_data);
409 return GRUB_ERR_NONE;
410 }
411
412 grub_err_t grub_disk_blocklist_read(void *chunklist, grub_uint64_t sector,
413 grub_uint64_t size, grub_uint32_t log_sector_size)
414 {
415 ventoy_img_chunk *last_chunk = NULL;
416 ventoy_img_chunk *new_chunk = NULL;
417 ventoy_img_chunk_list *chunk_list = (ventoy_img_chunk_list *)chunklist;
418
419 if (chunk_list->cur_chunk == 0)
420 {
421 chunk_list->chunk[0].img_start_sector = 0;
422 chunk_list->chunk[0].img_end_sector = (size >> 11) - 1;
423 chunk_list->chunk[0].disk_start_sector = sector;
424 chunk_list->chunk[0].disk_end_sector = sector + (size >> log_sector_size) - 1;
425 chunk_list->cur_chunk = 1;
426 return 0;
427 }
428
429 last_chunk = chunk_list->chunk + chunk_list->cur_chunk - 1;
430 if (last_chunk->disk_end_sector + 1 == sector)
431 {
432 last_chunk->img_end_sector += (size >> 11);
433 last_chunk->disk_end_sector += (size >> log_sector_size);
434 return 0;
435 }
436
437 if (chunk_list->cur_chunk == chunk_list->max_chunk)
438 {
439 new_chunk = grub_realloc(chunk_list->chunk, chunk_list->max_chunk * 2 * sizeof(ventoy_img_chunk));
440 if (NULL == new_chunk)
441 {
442 return -1;
443 }
444 chunk_list->chunk = new_chunk;
445 chunk_list->max_chunk *= 2;
446
447 /* issue: update last_chunk */
448 last_chunk = chunk_list->chunk + chunk_list->cur_chunk - 1;
449 }
450
451 new_chunk = chunk_list->chunk + chunk_list->cur_chunk;
452 new_chunk->img_start_sector = last_chunk->img_end_sector + 1;
453 new_chunk->img_end_sector = new_chunk->img_start_sector + (size >> 11) - 1;
454 new_chunk->disk_start_sector = sector;
455 new_chunk->disk_end_sector = sector + (size >> log_sector_size) - 1;
456
457 chunk_list->cur_chunk++;
458
459 return 0;
460 }
461
462 /* Read data from the disk. */
463 grub_err_t
464 grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
465 grub_off_t offset, grub_size_t size, void *buf)
466 {
467 if (disk->read_hook == (grub_disk_read_hook_t)grub_disk_blocklist_read)
468 {
469 return grub_disk_blocklist_read((ventoy_img_chunk_list *)disk->read_hook_data, sector, size, disk->log_sector_size);
470 }
471
472 /* First of all, check if the region is within the disk. */
473 if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
474 {
475 grub_error_push ();
476 grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n",
477 (unsigned long long) sector, grub_errmsg);
478 grub_error_pop ();
479 return grub_errno;
480 }
481
482 /* First read until first cache boundary. */
483 if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1)))
484 {
485 grub_disk_addr_t start_sector;
486 grub_size_t pos;
487 grub_err_t err;
488 grub_size_t len;
489
490 start_sector = sector & ~((grub_disk_addr_t) GRUB_DISK_CACHE_SIZE - 1);
491 pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
492 len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
493 - pos - offset);
494 if (len > size)
495 len = size;
496 err = grub_disk_read_small (disk, start_sector,
497 offset + pos, len, buf);
498 if (err)
499 return err;
500 buf = (char *) buf + len;
501 size -= len;
502 offset += len;
503 sector += (offset >> GRUB_DISK_SECTOR_BITS);
504 offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
505 }
506
507 /* Until SIZE is zero... */
508 while (size >= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS))
509 {
510 char *data = NULL;
511 grub_disk_addr_t agglomerate;
512 grub_err_t err;
513
514 /* agglomerate read until we find a first cached entry. */
515 for (agglomerate = 0; agglomerate
516 < (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS))
517 && agglomerate < disk->max_agglomerate;
518 agglomerate++)
519 {
520 data = grub_disk_cache_fetch (disk->dev->id, disk->id,
521 sector + (agglomerate
522 << GRUB_DISK_CACHE_BITS));
523 if (data)
524 break;
525 }
526
527 if (data)
528 {
529 grub_memcpy ((char *) buf
530 + (agglomerate << (GRUB_DISK_CACHE_BITS
531 + GRUB_DISK_SECTOR_BITS)),
532 data, GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
533 grub_disk_cache_unlock (disk->dev->id, disk->id,
534 sector + (agglomerate
535 << GRUB_DISK_CACHE_BITS));
536 }
537
538 if (agglomerate)
539 {
540 grub_disk_addr_t i;
541
542 err = (disk->dev->disk_read) (disk, transform_sector (disk, sector),
543 agglomerate << (GRUB_DISK_CACHE_BITS
544 + GRUB_DISK_SECTOR_BITS
545 - disk->log_sector_size),
546 buf);
547 if (err)
548 return err;
549
550 for (i = 0; i < agglomerate; i ++)
551 grub_disk_cache_store (disk->dev->id, disk->id,
552 sector + (i << GRUB_DISK_CACHE_BITS),
553 (char *) buf
554 + (i << (GRUB_DISK_CACHE_BITS
555 + GRUB_DISK_SECTOR_BITS)));
556
557
558 if (disk->read_hook)
559 (disk->read_hook) (sector, 0, agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS),
560 disk->read_hook_data);
561
562 sector += agglomerate << GRUB_DISK_CACHE_BITS;
563 size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
564 buf = (char *) buf
565 + (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
566 }
567
568 if (data)
569 {
570 if (disk->read_hook)
571 (disk->read_hook) (sector, 0, (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS),
572 disk->read_hook_data);
573 sector += GRUB_DISK_CACHE_SIZE;
574 buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
575 size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
576 }
577 }
578
579 /* And now read the last part. */
580 if (size)
581 {
582 grub_err_t err;
583 err = grub_disk_read_small (disk, sector, 0, size, buf);
584 if (err)
585 return err;
586 }
587
588 return grub_errno;
589 }
590
591 grub_uint64_t
592 grub_disk_get_size (grub_disk_t disk)
593 {
594 if (disk->partition)
595 return grub_partition_get_len (disk->partition);
596 else if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN)
597 return disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
598 else
599 return GRUB_DISK_SIZE_UNKNOWN;
600 }