disk.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  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. #include <grub/disk.h>
  19. #include <grub/err.h>
  20. #include <grub/mm.h>
  21. #include <grub/types.h>
  22. #include <grub/partition.h>
  23. #include <grub/misc.h>
  24. #include <grub/time.h>
  25. #include <grub/file.h>
  26. GRUB_EXPORT(grub_disk_dev_register);
  27. GRUB_EXPORT(grub_disk_dev_unregister);
  28. GRUB_EXPORT(grub_disk_dev_iterate);
  29. GRUB_EXPORT(grub_disk_open);
  30. GRUB_EXPORT(grub_disk_close);
  31. GRUB_EXPORT(grub_disk_read);
  32. GRUB_EXPORT(grub_disk_read_ex);
  33. GRUB_EXPORT(grub_disk_write);
  34. GRUB_EXPORT(grub_disk_get_size);
  35. GRUB_EXPORT(grub_disk_firmware_fini);
  36. GRUB_EXPORT(grub_disk_firmware_is_tainted);
  37. GRUB_EXPORT(grub_disk_ata_pass_through);
  38. #define GRUB_CACHE_TIMEOUT 2
  39. /* The last time the disk was used. */
  40. static grub_uint64_t grub_last_time = 0;
  41. /* Disk cache. */
  42. struct grub_disk_cache
  43. {
  44. enum grub_disk_dev_id dev_id;
  45. unsigned long disk_id;
  46. grub_disk_addr_t sector;
  47. char *data;
  48. int lock;
  49. };
  50. static struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
  51. void (*grub_disk_firmware_fini) (void);
  52. int grub_disk_firmware_is_tainted;
  53. grub_err_t (* grub_disk_ata_pass_through) (grub_disk_t,
  54. struct grub_disk_ata_pass_through_parms *);
  55. #if 0
  56. static unsigned long grub_disk_cache_hits;
  57. static unsigned long grub_disk_cache_misses;
  58. void
  59. grub_disk_cache_get_performance (unsigned long *hits, unsigned long *misses)
  60. {
  61. *hits = grub_disk_cache_hits;
  62. *misses = grub_disk_cache_misses;
  63. }
  64. #endif
  65. static unsigned
  66. grub_disk_cache_get_index (unsigned long dev_id, unsigned long disk_id,
  67. grub_disk_addr_t sector)
  68. {
  69. return ((dev_id * 524287UL + disk_id * 2606459UL
  70. + ((unsigned) (sector >> GRUB_DISK_CACHE_BITS)))
  71. % GRUB_DISK_CACHE_NUM);
  72. }
  73. static void
  74. grub_disk_cache_invalidate (unsigned long dev_id, unsigned long disk_id,
  75. grub_disk_addr_t sector)
  76. {
  77. unsigned index;
  78. struct grub_disk_cache *cache;
  79. sector &= ~(GRUB_DISK_CACHE_SIZE - 1);
  80. index = grub_disk_cache_get_index (dev_id, disk_id, sector);
  81. cache = grub_disk_cache_table + index;
  82. if (cache->dev_id == dev_id && cache->disk_id == disk_id
  83. && cache->sector == sector && cache->data)
  84. {
  85. cache->lock = 1;
  86. grub_free (cache->data);
  87. cache->data = 0;
  88. cache->lock = 0;
  89. }
  90. }
  91. void
  92. grub_disk_cache_invalidate_all (void)
  93. {
  94. unsigned i;
  95. for (i = 0; i < GRUB_DISK_CACHE_NUM; i++)
  96. {
  97. struct grub_disk_cache *cache = grub_disk_cache_table + i;
  98. if (cache->data && ! cache->lock)
  99. {
  100. grub_free (cache->data);
  101. cache->data = 0;
  102. }
  103. }
  104. }
  105. static char *
  106. grub_disk_cache_fetch (unsigned long dev_id, unsigned long disk_id,
  107. grub_disk_addr_t sector)
  108. {
  109. struct grub_disk_cache *cache;
  110. unsigned index;
  111. index = grub_disk_cache_get_index (dev_id, disk_id, sector);
  112. cache = grub_disk_cache_table + index;
  113. if (cache->dev_id == dev_id && cache->disk_id == disk_id
  114. && cache->sector == sector)
  115. {
  116. cache->lock = 1;
  117. #if 0
  118. grub_disk_cache_hits++;
  119. #endif
  120. return cache->data;
  121. }
  122. #if 0
  123. grub_disk_cache_misses++;
  124. #endif
  125. return 0;
  126. }
  127. static void
  128. grub_disk_cache_unlock (unsigned long dev_id, unsigned long disk_id,
  129. grub_disk_addr_t sector)
  130. {
  131. struct grub_disk_cache *cache;
  132. unsigned index;
  133. index = grub_disk_cache_get_index (dev_id, disk_id, sector);
  134. cache = grub_disk_cache_table + index;
  135. if (cache->dev_id == dev_id && cache->disk_id == disk_id
  136. && cache->sector == sector)
  137. cache->lock = 0;
  138. }
  139. static grub_err_t
  140. grub_disk_cache_store (unsigned long dev_id, unsigned long disk_id,
  141. grub_disk_addr_t sector, const char *data)
  142. {
  143. unsigned index;
  144. struct grub_disk_cache *cache;
  145. index = grub_disk_cache_get_index (dev_id, disk_id, sector);
  146. cache = grub_disk_cache_table + index;
  147. cache->lock = 1;
  148. grub_free (cache->data);
  149. cache->data = 0;
  150. cache->lock = 0;
  151. cache->data = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
  152. if (! cache->data)
  153. return grub_errno;
  154. grub_memcpy (cache->data, data,
  155. GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
  156. cache->dev_id = dev_id;
  157. cache->disk_id = disk_id;
  158. cache->sector = sector;
  159. return GRUB_ERR_NONE;
  160. }
  161. static grub_disk_dev_t grub_disk_dev_list;
  162. void
  163. grub_disk_dev_register (grub_disk_dev_t dev)
  164. {
  165. dev->next = grub_disk_dev_list;
  166. grub_disk_dev_list = dev;
  167. }
  168. void
  169. grub_disk_dev_unregister (grub_disk_dev_t dev)
  170. {
  171. grub_disk_dev_t *p, q;
  172. for (p = &grub_disk_dev_list, q = *p; q; p = &(q->next), q = q->next)
  173. if (q == dev)
  174. {
  175. *p = q->next;
  176. break;
  177. }
  178. }
  179. int
  180. grub_disk_dev_iterate (int (*hook) (const char *name, void *closure),
  181. void *closure)
  182. {
  183. grub_disk_dev_t p;
  184. for (p = grub_disk_dev_list; p; p = p->next)
  185. if (p->iterate && (p->iterate) (hook, closure))
  186. return 1;
  187. return 0;
  188. }
  189. /* Return the location of the first ',', if any, which is not
  190. escaped by a '\'. */
  191. static const char *
  192. find_part_sep (const char *name)
  193. {
  194. const char *p = name;
  195. char c;
  196. while ((c = *p++) != '\0')
  197. {
  198. if (c == '\\' && *p == ',')
  199. p++;
  200. else if (c == ',')
  201. return p - 1;
  202. }
  203. return NULL;
  204. }
  205. grub_disk_t
  206. grub_disk_open (const char *name)
  207. {
  208. const char *p;
  209. grub_disk_t disk;
  210. grub_disk_dev_t dev;
  211. char *raw = (char *) name;
  212. grub_uint64_t current_time;
  213. grub_dprintf ("disk", "Opening `%s'...\n", name);
  214. disk = (grub_disk_t) grub_zalloc (sizeof (*disk));
  215. if (! disk)
  216. return 0;
  217. disk->name = grub_strdup (name);
  218. if (! disk->name)
  219. goto fail;
  220. p = find_part_sep (name);
  221. if (p)
  222. {
  223. grub_size_t len = p - name;
  224. raw = grub_malloc (len + 1);
  225. if (! raw)
  226. goto fail;
  227. grub_memcpy (raw, name, len);
  228. raw[len] = '\0';
  229. }
  230. for (dev = grub_disk_dev_list; dev; dev = dev->next)
  231. {
  232. if ((dev->open) (raw, disk) == GRUB_ERR_NONE)
  233. break;
  234. else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
  235. grub_errno = GRUB_ERR_NONE;
  236. else
  237. goto fail;
  238. }
  239. if (! dev)
  240. {
  241. grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such disk");
  242. goto fail;
  243. }
  244. if (p && ! disk->has_partitions)
  245. {
  246. grub_error (GRUB_ERR_BAD_DEVICE, "no partition on this disk");
  247. goto fail;
  248. }
  249. disk->dev = dev;
  250. if (p)
  251. {
  252. disk->partition = grub_partition_probe (disk, p + 1);
  253. if (! disk->partition)
  254. {
  255. grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such partition");
  256. goto fail;
  257. }
  258. }
  259. /* The cache will be invalidated about 2 seconds after a device was
  260. closed. */
  261. current_time = grub_get_time_ms ();
  262. if (current_time > (grub_last_time
  263. + GRUB_CACHE_TIMEOUT * 1000))
  264. grub_disk_cache_invalidate_all ();
  265. grub_last_time = current_time;
  266. fail:
  267. if (raw && raw != name)
  268. grub_free (raw);
  269. if (grub_errno != GRUB_ERR_NONE)
  270. {
  271. grub_error_push ();
  272. grub_dprintf ("disk", "Opening `%s' failed.\n", name);
  273. grub_error_pop ();
  274. grub_disk_close (disk);
  275. return 0;
  276. }
  277. return disk;
  278. }
  279. void
  280. grub_disk_close (grub_disk_t disk)
  281. {
  282. grub_partition_t part;
  283. grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
  284. if (disk->dev && disk->dev->close)
  285. (disk->dev->close) (disk);
  286. /* Reset the timer. */
  287. grub_last_time = grub_get_time_ms ();
  288. while (disk->partition)
  289. {
  290. part = disk->partition->parent;
  291. grub_free (disk->partition);
  292. disk->partition = part;
  293. }
  294. grub_free ((void *) disk->name);
  295. grub_free (disk);
  296. }
  297. /* This function performs three tasks:
  298. - Make sectors disk relative from partition relative.
  299. - Normalize offset to be less than the sector size.
  300. - Verify that the range is inside the partition. */
  301. static grub_err_t
  302. grub_disk_adjust_range (grub_disk_t disk, grub_disk_addr_t *sector,
  303. grub_off_t *offset, grub_size_t size)
  304. {
  305. grub_partition_t part;
  306. *sector += *offset >> GRUB_DISK_SECTOR_BITS;
  307. *offset &= GRUB_DISK_SECTOR_SIZE - 1;
  308. for (part = disk->partition; part; part = part->parent)
  309. {
  310. grub_disk_addr_t start;
  311. grub_uint64_t len;
  312. start = part->start;
  313. len = part->len;
  314. if (*sector >= len
  315. || len - *sector < ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1)
  316. >> GRUB_DISK_SECTOR_BITS))
  317. return grub_error (GRUB_ERR_OUT_OF_RANGE, "out of partition");
  318. *sector += start;
  319. }
  320. if (disk->total_sectors <= *sector
  321. || ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1)
  322. >> GRUB_DISK_SECTOR_BITS) > disk->total_sectors - *sector)
  323. return grub_error (GRUB_ERR_OUT_OF_RANGE, "out of disk");
  324. return GRUB_ERR_NONE;
  325. }
  326. /* Read data from the disk. */
  327. grub_err_t
  328. grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
  329. grub_off_t offset, grub_size_t size, void *buf)
  330. {
  331. char *tmp_buf;
  332. unsigned real_offset;
  333. /* First of all, check if the region is within the disk. */
  334. if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
  335. {
  336. grub_error_push ();
  337. grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n",
  338. (unsigned long long) sector, grub_errmsg);
  339. grub_error_pop ();
  340. return grub_errno;
  341. }
  342. real_offset = offset;
  343. /* Allocate a temporary buffer. */
  344. tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
  345. if (! tmp_buf)
  346. return grub_errno;
  347. /* Until SIZE is zero... */
  348. while (size)
  349. {
  350. char *data;
  351. grub_disk_addr_t start_sector;
  352. grub_size_t len;
  353. grub_size_t pos;
  354. /* For reading bulk data. */
  355. start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1);
  356. pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
  357. len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
  358. - pos - real_offset);
  359. if (len > size)
  360. len = size;
  361. /* Fetch the cache. */
  362. data = grub_disk_cache_fetch (disk->dev->id, disk->id, start_sector);
  363. if (data)
  364. {
  365. /* Just copy it! */
  366. if (buf)
  367. grub_memcpy (buf, data + pos + real_offset, len);
  368. grub_disk_cache_unlock (disk->dev->id, disk->id, start_sector);
  369. }
  370. else
  371. {
  372. /* Otherwise read data from the disk actually. */
  373. if (start_sector + GRUB_DISK_CACHE_SIZE > disk->total_sectors
  374. || (disk->dev->read) (disk, start_sector,
  375. GRUB_DISK_CACHE_SIZE, tmp_buf)
  376. != GRUB_ERR_NONE)
  377. {
  378. /* Uggh... Failed. Instead, just read necessary data. */
  379. unsigned num;
  380. char *p;
  381. grub_errno = GRUB_ERR_NONE;
  382. num = ((size + real_offset + GRUB_DISK_SECTOR_SIZE - 1)
  383. >> GRUB_DISK_SECTOR_BITS);
  384. p = grub_realloc (tmp_buf, num << GRUB_DISK_SECTOR_BITS);
  385. if (!p)
  386. goto finish;
  387. tmp_buf = p;
  388. if ((disk->dev->read) (disk, sector, num, tmp_buf))
  389. {
  390. grub_error_push ();
  391. grub_dprintf ("disk", "%s read failed\n", disk->name);
  392. grub_error_pop ();
  393. goto finish;
  394. }
  395. if (buf)
  396. grub_memcpy (buf, tmp_buf + real_offset, size);
  397. /* Call the read hook, if any. */
  398. if (disk->read_hook)
  399. while (size)
  400. {
  401. grub_size_t to_read;
  402. to_read = size;
  403. if (real_offset + to_read > GRUB_DISK_SECTOR_SIZE)
  404. to_read = GRUB_DISK_SECTOR_SIZE - real_offset;
  405. (disk->read_hook) (sector, real_offset,
  406. to_read, disk->closure);
  407. if (grub_errno != GRUB_ERR_NONE)
  408. goto finish;
  409. sector++;
  410. size -= to_read;
  411. real_offset = 0;
  412. }
  413. /* This must be the end. */
  414. goto finish;
  415. }
  416. /* Copy it and store it in the disk cache. */
  417. if (buf)
  418. grub_memcpy (buf, tmp_buf + pos + real_offset, len);
  419. grub_disk_cache_store (disk->dev->id, disk->id,
  420. start_sector, tmp_buf);
  421. }
  422. /* Call the read hook, if any. */
  423. if (disk->read_hook)
  424. {
  425. grub_disk_addr_t s = sector;
  426. grub_size_t l = len;
  427. while (l)
  428. {
  429. (disk->read_hook) (s, real_offset,
  430. ((l > GRUB_DISK_SECTOR_SIZE)
  431. ? GRUB_DISK_SECTOR_SIZE
  432. : l), disk->closure);
  433. if (l < GRUB_DISK_SECTOR_SIZE - real_offset)
  434. break;
  435. s++;
  436. l -= GRUB_DISK_SECTOR_SIZE - real_offset;
  437. real_offset = 0;
  438. }
  439. }
  440. sector = start_sector + GRUB_DISK_CACHE_SIZE;
  441. if (buf)
  442. buf = (char *) buf + len;
  443. size -= len;
  444. real_offset = 0;
  445. }
  446. finish:
  447. grub_free (tmp_buf);
  448. return grub_errno;
  449. }
  450. grub_err_t
  451. grub_disk_read_ex (grub_disk_t disk, grub_disk_addr_t sector,
  452. grub_off_t offset, grub_size_t size, void *buf, int flags)
  453. {
  454. unsigned real_offset;
  455. if (! flags)
  456. return grub_disk_read (disk, sector, offset, size, buf);
  457. if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
  458. return grub_errno;
  459. real_offset = offset;
  460. while (size)
  461. {
  462. char tmp_buf[GRUB_DISK_SECTOR_SIZE];
  463. grub_size_t len;
  464. if ((real_offset != 0) || (size < GRUB_DISK_SECTOR_SIZE))
  465. {
  466. len = GRUB_DISK_SECTOR_SIZE - real_offset;
  467. if (len > size)
  468. len = size;
  469. if (buf)
  470. {
  471. if ((disk->dev->read) (disk, sector, 1, tmp_buf) != GRUB_ERR_NONE)
  472. break;
  473. grub_memcpy (buf, tmp_buf + real_offset, len);
  474. }
  475. if (disk->read_hook)
  476. (disk->read_hook) (sector, real_offset, len, disk->closure);
  477. sector++;
  478. real_offset = 0;
  479. }
  480. else
  481. {
  482. grub_size_t n;
  483. len = size & ~(GRUB_DISK_SECTOR_SIZE - 1);
  484. n = size >> GRUB_DISK_SECTOR_BITS;
  485. if ((buf) &&
  486. ((disk->dev->read) (disk, sector, n, buf) != GRUB_ERR_NONE))
  487. break;
  488. if (disk->read_hook)
  489. {
  490. while (n)
  491. {
  492. (disk->read_hook) (sector++, 0, GRUB_DISK_SECTOR_SIZE,
  493. disk->closure);
  494. n--;
  495. }
  496. }
  497. else
  498. sector += n;
  499. }
  500. if (buf)
  501. buf = (char *) buf + len;
  502. size -= len;
  503. }
  504. return grub_errno;
  505. }
  506. grub_err_t
  507. grub_disk_write (grub_disk_t disk, grub_disk_addr_t sector,
  508. grub_off_t offset, grub_size_t size, const void *buf)
  509. {
  510. unsigned real_offset;
  511. grub_dprintf ("disk", "Writing `%s'...\n", disk->name);
  512. if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
  513. return grub_errno;
  514. real_offset = offset;
  515. while (size)
  516. {
  517. if (real_offset != 0 || (size < GRUB_DISK_SECTOR_SIZE && size != 0))
  518. {
  519. char tmp_buf[GRUB_DISK_SECTOR_SIZE];
  520. grub_size_t len;
  521. grub_partition_t part;
  522. part = disk->partition;
  523. disk->partition = 0;
  524. if (grub_disk_read (disk, sector, 0, GRUB_DISK_SECTOR_SIZE, tmp_buf)
  525. != GRUB_ERR_NONE)
  526. {
  527. disk->partition = part;
  528. goto finish;
  529. }
  530. disk->partition = part;
  531. len = GRUB_DISK_SECTOR_SIZE - real_offset;
  532. if (len > size)
  533. len = size;
  534. grub_memcpy (tmp_buf + real_offset, buf, len);
  535. grub_disk_cache_invalidate (disk->dev->id, disk->id, sector);
  536. if ((disk->dev->write) (disk, sector, 1, tmp_buf) != GRUB_ERR_NONE)
  537. goto finish;
  538. sector++;
  539. buf = (char *) buf + len;
  540. size -= len;
  541. real_offset = 0;
  542. }
  543. else
  544. {
  545. grub_size_t len;
  546. grub_size_t n;
  547. len = size & ~(GRUB_DISK_SECTOR_SIZE - 1);
  548. n = size >> GRUB_DISK_SECTOR_BITS;
  549. if ((disk->dev->write) (disk, sector, n, buf) != GRUB_ERR_NONE)
  550. goto finish;
  551. while (n--)
  552. grub_disk_cache_invalidate (disk->dev->id, disk->id, sector++);
  553. buf = (char *) buf + len;
  554. size -= len;
  555. }
  556. }
  557. finish:
  558. return grub_errno;
  559. }
  560. grub_uint64_t
  561. grub_disk_get_size (grub_disk_t disk)
  562. {
  563. if (disk->partition)
  564. return grub_partition_get_len (disk->partition);
  565. else
  566. return disk->total_sectors;
  567. }