disk.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  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. #include <grub/i18n.h>
  27. #define GRUB_CACHE_TIMEOUT 2
  28. /* The last time the disk was used. */
  29. static grub_uint64_t grub_last_time = 0;
  30. struct grub_disk_cache grub_disk_cache_table[GRUB_DISK_CACHE_NUM];
  31. void (*grub_disk_firmware_fini) (void);
  32. int grub_disk_firmware_is_tainted;
  33. #if DISK_CACHE_STATS
  34. static unsigned long grub_disk_cache_hits;
  35. static unsigned long grub_disk_cache_misses;
  36. void
  37. grub_disk_cache_get_performance (unsigned long *hits, unsigned long *misses)
  38. {
  39. *hits = grub_disk_cache_hits;
  40. *misses = grub_disk_cache_misses;
  41. }
  42. #endif
  43. grub_err_t (*grub_disk_write_weak) (grub_disk_t disk,
  44. grub_disk_addr_t sector,
  45. grub_off_t offset,
  46. grub_size_t size,
  47. const void *buf);
  48. #include "disk_common.c"
  49. void
  50. grub_disk_cache_invalidate_all (void)
  51. {
  52. unsigned i;
  53. for (i = 0; i < GRUB_DISK_CACHE_NUM; i++)
  54. {
  55. struct grub_disk_cache *cache = grub_disk_cache_table + i;
  56. if (cache->data && ! cache->lock)
  57. {
  58. grub_free (cache->data);
  59. cache->data = 0;
  60. }
  61. }
  62. }
  63. static char *
  64. grub_disk_cache_fetch (unsigned long dev_id, unsigned long disk_id,
  65. grub_disk_addr_t sector)
  66. {
  67. struct grub_disk_cache *cache;
  68. unsigned cache_index;
  69. cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
  70. cache = grub_disk_cache_table + cache_index;
  71. if (cache->dev_id == dev_id && cache->disk_id == disk_id
  72. && cache->sector == sector)
  73. {
  74. cache->lock = 1;
  75. #if DISK_CACHE_STATS
  76. grub_disk_cache_hits++;
  77. #endif
  78. return cache->data;
  79. }
  80. #if DISK_CACHE_STATS
  81. grub_disk_cache_misses++;
  82. #endif
  83. return 0;
  84. }
  85. static void
  86. grub_disk_cache_unlock (unsigned long dev_id, unsigned long disk_id,
  87. grub_disk_addr_t sector)
  88. {
  89. struct grub_disk_cache *cache;
  90. unsigned cache_index;
  91. cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
  92. cache = grub_disk_cache_table + cache_index;
  93. if (cache->dev_id == dev_id && cache->disk_id == disk_id
  94. && cache->sector == sector)
  95. cache->lock = 0;
  96. }
  97. static grub_err_t
  98. grub_disk_cache_store (unsigned long dev_id, unsigned long disk_id,
  99. grub_disk_addr_t sector, const char *data)
  100. {
  101. unsigned cache_index;
  102. struct grub_disk_cache *cache;
  103. cache_index = grub_disk_cache_get_index (dev_id, disk_id, sector);
  104. cache = grub_disk_cache_table + cache_index;
  105. cache->lock = 1;
  106. grub_free (cache->data);
  107. cache->data = 0;
  108. cache->lock = 0;
  109. cache->data = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
  110. if (! cache->data)
  111. return grub_errno;
  112. grub_memcpy (cache->data, data,
  113. GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
  114. cache->dev_id = dev_id;
  115. cache->disk_id = disk_id;
  116. cache->sector = sector;
  117. return GRUB_ERR_NONE;
  118. }
  119. grub_disk_dev_t grub_disk_dev_list;
  120. void
  121. grub_disk_dev_register (grub_disk_dev_t dev)
  122. {
  123. dev->next = grub_disk_dev_list;
  124. grub_disk_dev_list = dev;
  125. }
  126. void
  127. grub_disk_dev_unregister (grub_disk_dev_t dev)
  128. {
  129. grub_disk_dev_t *p, q;
  130. for (p = &grub_disk_dev_list, q = *p; q; p = &(q->next), q = q->next)
  131. if (q == dev)
  132. {
  133. *p = q->next;
  134. break;
  135. }
  136. }
  137. /* Return the location of the first ',', if any, which is not
  138. escaped by a '\'. */
  139. static const char *
  140. find_part_sep (const char *name)
  141. {
  142. const char *p = name;
  143. char c;
  144. while ((c = *p++) != '\0')
  145. {
  146. if (c == '\\' && *p == ',')
  147. p++;
  148. else if (c == ',')
  149. return p - 1;
  150. }
  151. return NULL;
  152. }
  153. grub_disk_t
  154. grub_disk_open (const char *name)
  155. {
  156. const char *p;
  157. grub_disk_t disk;
  158. grub_disk_dev_t dev;
  159. char *raw = (char *) name;
  160. grub_uint64_t current_time;
  161. grub_dprintf ("disk", "Opening `%s'...\n", name);
  162. disk = (grub_disk_t) grub_zalloc (sizeof (*disk));
  163. if (! disk)
  164. return 0;
  165. disk->log_sector_size = GRUB_DISK_SECTOR_BITS;
  166. /* Default 1MiB of maximum agglomerate. */
  167. disk->max_agglomerate = 1048576 >> (GRUB_DISK_SECTOR_BITS
  168. + GRUB_DISK_CACHE_BITS);
  169. p = find_part_sep (name);
  170. if (p)
  171. {
  172. grub_size_t len = p - name;
  173. raw = grub_malloc (len + 1);
  174. if (! raw)
  175. goto fail;
  176. grub_memcpy (raw, name, len);
  177. raw[len] = '\0';
  178. disk->name = grub_strdup (raw);
  179. }
  180. else
  181. disk->name = grub_strdup (name);
  182. if (! disk->name)
  183. goto fail;
  184. for (dev = grub_disk_dev_list; dev; dev = dev->next)
  185. {
  186. if ((dev->open) (raw, disk) == GRUB_ERR_NONE)
  187. break;
  188. else if (grub_errno == GRUB_ERR_UNKNOWN_DEVICE)
  189. grub_errno = GRUB_ERR_NONE;
  190. else
  191. goto fail;
  192. }
  193. if (! dev)
  194. {
  195. grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"),
  196. name);
  197. goto fail;
  198. }
  199. if (disk->log_sector_size > GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS
  200. || disk->log_sector_size < GRUB_DISK_SECTOR_BITS)
  201. {
  202. grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
  203. "sector sizes of %d bytes aren't supported yet",
  204. (1 << disk->log_sector_size));
  205. goto fail;
  206. }
  207. disk->dev = dev;
  208. if (p)
  209. {
  210. disk->partition = grub_partition_probe (disk, p + 1);
  211. if (! disk->partition)
  212. {
  213. /* TRANSLATORS: It means that the specified partition e.g.
  214. hd0,msdos1=/dev/sda1 doesn't exist. */
  215. grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such partition"));
  216. goto fail;
  217. }
  218. }
  219. /* The cache will be invalidated about 2 seconds after a device was
  220. closed. */
  221. current_time = grub_get_time_ms ();
  222. if (current_time > (grub_last_time
  223. + GRUB_CACHE_TIMEOUT * 1000))
  224. grub_disk_cache_invalidate_all ();
  225. grub_last_time = current_time;
  226. fail:
  227. if (raw && raw != name)
  228. grub_free (raw);
  229. if (grub_errno != GRUB_ERR_NONE)
  230. {
  231. grub_error_push ();
  232. grub_dprintf ("disk", "Opening `%s' failed.\n", name);
  233. grub_error_pop ();
  234. grub_disk_close (disk);
  235. return 0;
  236. }
  237. return disk;
  238. }
  239. void
  240. grub_disk_close (grub_disk_t disk)
  241. {
  242. grub_partition_t part;
  243. grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
  244. if (disk->dev && disk->dev->close)
  245. (disk->dev->close) (disk);
  246. /* Reset the timer. */
  247. grub_last_time = grub_get_time_ms ();
  248. while (disk->partition)
  249. {
  250. part = disk->partition->parent;
  251. grub_free (disk->partition);
  252. disk->partition = part;
  253. }
  254. grub_free ((void *) disk->name);
  255. grub_free (disk);
  256. }
  257. /* Small read (less than cache size and not pass across cache unit boundaries).
  258. sector is already adjusted and is divisible by cache unit size.
  259. */
  260. static grub_err_t
  261. grub_disk_read_small_real (grub_disk_t disk, grub_disk_addr_t sector,
  262. grub_off_t offset, grub_size_t size, void *buf)
  263. {
  264. char *data;
  265. char *tmp_buf;
  266. /* Fetch the cache. */
  267. data = grub_disk_cache_fetch (disk->dev->id, disk->id, sector);
  268. if (data)
  269. {
  270. /* Just copy it! */
  271. grub_memcpy (buf, data + offset, size);
  272. grub_disk_cache_unlock (disk->dev->id, disk->id, sector);
  273. return GRUB_ERR_NONE;
  274. }
  275. /* Allocate a temporary buffer. */
  276. tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
  277. if (! tmp_buf)
  278. return grub_errno;
  279. /* Otherwise read data from the disk actually. */
  280. if (disk->total_sectors == GRUB_DISK_SIZE_UNKNOWN
  281. || sector + GRUB_DISK_CACHE_SIZE
  282. < (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
  283. {
  284. grub_err_t err;
  285. err = (disk->dev->read) (disk, transform_sector (disk, sector),
  286. 1U << (GRUB_DISK_CACHE_BITS
  287. + GRUB_DISK_SECTOR_BITS
  288. - disk->log_sector_size), tmp_buf);
  289. if (!err)
  290. {
  291. /* Copy it and store it in the disk cache. */
  292. grub_memcpy (buf, tmp_buf + offset, size);
  293. grub_disk_cache_store (disk->dev->id, disk->id,
  294. sector, tmp_buf);
  295. grub_free (tmp_buf);
  296. return GRUB_ERR_NONE;
  297. }
  298. }
  299. grub_free (tmp_buf);
  300. grub_errno = GRUB_ERR_NONE;
  301. {
  302. /* Uggh... Failed. Instead, just read necessary data. */
  303. unsigned num;
  304. grub_disk_addr_t aligned_sector;
  305. sector += (offset >> GRUB_DISK_SECTOR_BITS);
  306. offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
  307. aligned_sector = (sector & ~((1ULL << (disk->log_sector_size
  308. - GRUB_DISK_SECTOR_BITS))
  309. - 1));
  310. offset += ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS);
  311. num = ((size + offset + (1ULL << (disk->log_sector_size))
  312. - 1) >> (disk->log_sector_size));
  313. tmp_buf = grub_malloc (num << disk->log_sector_size);
  314. if (!tmp_buf)
  315. return grub_errno;
  316. if ((disk->dev->read) (disk, transform_sector (disk, aligned_sector),
  317. num, tmp_buf))
  318. {
  319. grub_error_push ();
  320. grub_dprintf ("disk", "%s read failed\n", disk->name);
  321. grub_error_pop ();
  322. grub_free (tmp_buf);
  323. return grub_errno;
  324. }
  325. grub_memcpy (buf, tmp_buf + offset, size);
  326. grub_free (tmp_buf);
  327. return GRUB_ERR_NONE;
  328. }
  329. }
  330. static grub_err_t
  331. grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
  332. grub_off_t offset, grub_size_t size, void *buf)
  333. {
  334. grub_err_t err;
  335. err = grub_disk_read_small_real (disk, sector, offset, size, buf);
  336. if (err)
  337. return err;
  338. if (disk->read_hook)
  339. (disk->read_hook) (sector + (offset >> GRUB_DISK_SECTOR_BITS),
  340. offset & (GRUB_DISK_SECTOR_SIZE - 1),
  341. size, disk->read_hook_data);
  342. return GRUB_ERR_NONE;
  343. }
  344. /* Read data from the disk. */
  345. grub_err_t
  346. grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
  347. grub_off_t offset, grub_size_t size, void *buf)
  348. {
  349. /* First of all, check if the region is within the disk. */
  350. if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
  351. {
  352. grub_error_push ();
  353. grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n",
  354. (unsigned long long) sector, grub_errmsg);
  355. grub_error_pop ();
  356. return grub_errno;
  357. }
  358. /* First read until first cache boundary. */
  359. if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1)))
  360. {
  361. grub_disk_addr_t start_sector;
  362. grub_size_t pos;
  363. grub_err_t err;
  364. grub_size_t len;
  365. start_sector = sector & ~((grub_disk_addr_t) GRUB_DISK_CACHE_SIZE - 1);
  366. pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
  367. len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
  368. - pos - offset);
  369. if (len > size)
  370. len = size;
  371. err = grub_disk_read_small (disk, start_sector,
  372. offset + pos, len, buf);
  373. if (err)
  374. return err;
  375. buf = (char *) buf + len;
  376. size -= len;
  377. offset += len;
  378. sector += (offset >> GRUB_DISK_SECTOR_BITS);
  379. offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
  380. }
  381. /* Until SIZE is zero... */
  382. while (size >= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS))
  383. {
  384. char *data = NULL;
  385. grub_disk_addr_t agglomerate;
  386. grub_err_t err;
  387. /* agglomerate read until we find a first cached entry. */
  388. for (agglomerate = 0; agglomerate
  389. < (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS))
  390. && agglomerate < disk->max_agglomerate;
  391. agglomerate++)
  392. {
  393. data = grub_disk_cache_fetch (disk->dev->id, disk->id,
  394. sector + (agglomerate
  395. << GRUB_DISK_CACHE_BITS));
  396. if (data)
  397. break;
  398. }
  399. if (data)
  400. {
  401. grub_memcpy ((char *) buf
  402. + (agglomerate << (GRUB_DISK_CACHE_BITS
  403. + GRUB_DISK_SECTOR_BITS)),
  404. data, GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
  405. grub_disk_cache_unlock (disk->dev->id, disk->id,
  406. sector + (agglomerate
  407. << GRUB_DISK_CACHE_BITS));
  408. }
  409. if (agglomerate)
  410. {
  411. grub_disk_addr_t i;
  412. err = (disk->dev->read) (disk, transform_sector (disk, sector),
  413. agglomerate << (GRUB_DISK_CACHE_BITS
  414. + GRUB_DISK_SECTOR_BITS
  415. - disk->log_sector_size),
  416. buf);
  417. if (err)
  418. return err;
  419. for (i = 0; i < agglomerate; i ++)
  420. grub_disk_cache_store (disk->dev->id, disk->id,
  421. sector + (i << GRUB_DISK_CACHE_BITS),
  422. (char *) buf
  423. + (i << (GRUB_DISK_CACHE_BITS
  424. + GRUB_DISK_SECTOR_BITS)));
  425. if (disk->read_hook)
  426. (disk->read_hook) (sector, 0, agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS),
  427. disk->read_hook_data);
  428. sector += agglomerate << GRUB_DISK_CACHE_BITS;
  429. size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
  430. buf = (char *) buf
  431. + (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
  432. }
  433. if (data)
  434. {
  435. if (disk->read_hook)
  436. (disk->read_hook) (sector, 0, (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS),
  437. disk->read_hook_data);
  438. sector += GRUB_DISK_CACHE_SIZE;
  439. buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
  440. size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
  441. }
  442. }
  443. /* And now read the last part. */
  444. if (size)
  445. {
  446. grub_err_t err;
  447. err = grub_disk_read_small (disk, sector, 0, size, buf);
  448. if (err)
  449. return err;
  450. }
  451. return grub_errno;
  452. }
  453. grub_uint64_t
  454. grub_disk_get_size (grub_disk_t disk)
  455. {
  456. if (disk->partition)
  457. return grub_partition_get_len (disk->partition);
  458. else if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN)
  459. return disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
  460. else
  461. return GRUB_DISK_SIZE_UNKNOWN;
  462. }