disk.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  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->disk_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. if (disk == NULL)
  244. return;
  245. grub_dprintf ("disk", "Closing `%s'.\n", disk->name);
  246. if (disk->dev && disk->dev->disk_close)
  247. (disk->dev->disk_close) (disk);
  248. /* Reset the timer. */
  249. grub_last_time = grub_get_time_ms ();
  250. while (disk->partition)
  251. {
  252. part = disk->partition->parent;
  253. grub_free (disk->partition);
  254. disk->partition = part;
  255. }
  256. grub_free ((void *) disk->name);
  257. grub_free (disk);
  258. }
  259. /* Small read (less than cache size and not pass across cache unit boundaries).
  260. sector is already adjusted and is divisible by cache unit size.
  261. */
  262. static grub_err_t
  263. grub_disk_read_small_real (grub_disk_t disk, grub_disk_addr_t sector,
  264. grub_off_t offset, grub_size_t size, void *buf)
  265. {
  266. char *data;
  267. char *tmp_buf;
  268. /* Fetch the cache. */
  269. data = grub_disk_cache_fetch (disk->dev->id, disk->id, sector);
  270. if (data)
  271. {
  272. /* Just copy it! */
  273. grub_memcpy (buf, data + offset, size);
  274. grub_disk_cache_unlock (disk->dev->id, disk->id, sector);
  275. return GRUB_ERR_NONE;
  276. }
  277. /* Allocate a temporary buffer. */
  278. tmp_buf = grub_malloc (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS);
  279. if (! tmp_buf)
  280. return grub_errno;
  281. /* Otherwise read data from the disk actually. */
  282. if (disk->total_sectors == GRUB_DISK_SIZE_UNKNOWN
  283. || sector + GRUB_DISK_CACHE_SIZE
  284. < (disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
  285. {
  286. grub_err_t err;
  287. err = (disk->dev->disk_read) (disk, grub_disk_to_native_sector (disk, sector),
  288. 1U << (GRUB_DISK_CACHE_BITS
  289. + GRUB_DISK_SECTOR_BITS
  290. - disk->log_sector_size), tmp_buf);
  291. if (!err)
  292. {
  293. /* Copy it and store it in the disk cache. */
  294. grub_memcpy (buf, tmp_buf + offset, size);
  295. grub_disk_cache_store (disk->dev->id, disk->id,
  296. sector, tmp_buf);
  297. grub_free (tmp_buf);
  298. return GRUB_ERR_NONE;
  299. }
  300. }
  301. grub_free (tmp_buf);
  302. grub_errno = GRUB_ERR_NONE;
  303. {
  304. /* Uggh... Failed. Instead, just read necessary data. */
  305. unsigned num;
  306. grub_disk_addr_t aligned_sector;
  307. sector += (offset >> GRUB_DISK_SECTOR_BITS);
  308. offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
  309. aligned_sector = (sector & ~((1ULL << (disk->log_sector_size
  310. - GRUB_DISK_SECTOR_BITS))
  311. - 1));
  312. offset += ((sector - aligned_sector) << GRUB_DISK_SECTOR_BITS);
  313. num = ((size + offset + (1ULL << (disk->log_sector_size))
  314. - 1) >> (disk->log_sector_size));
  315. tmp_buf = grub_malloc (num << disk->log_sector_size);
  316. if (!tmp_buf)
  317. return grub_errno;
  318. if ((disk->dev->disk_read) (disk, grub_disk_to_native_sector (disk, aligned_sector),
  319. num, tmp_buf))
  320. {
  321. grub_error_push ();
  322. grub_dprintf ("disk", "%s read failed\n", disk->name);
  323. grub_error_pop ();
  324. grub_free (tmp_buf);
  325. return grub_errno;
  326. }
  327. grub_memcpy (buf, tmp_buf + offset, size);
  328. grub_free (tmp_buf);
  329. return GRUB_ERR_NONE;
  330. }
  331. }
  332. static grub_err_t
  333. grub_disk_read_small (grub_disk_t disk, grub_disk_addr_t sector,
  334. grub_off_t offset, grub_size_t size, void *buf)
  335. {
  336. grub_err_t err;
  337. err = grub_disk_read_small_real (disk, sector, offset, size, buf);
  338. if (err)
  339. return err;
  340. if (disk->read_hook)
  341. err = (disk->read_hook) (sector + (offset >> GRUB_DISK_SECTOR_BITS),
  342. offset & (GRUB_DISK_SECTOR_SIZE - 1),
  343. size, buf, disk->read_hook_data);
  344. return err;
  345. }
  346. /* Read data from the disk. */
  347. grub_err_t
  348. grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector,
  349. grub_off_t offset, grub_size_t size, void *buf)
  350. {
  351. /* First of all, check if the region is within the disk. */
  352. if (grub_disk_adjust_range (disk, &sector, &offset, size) != GRUB_ERR_NONE)
  353. {
  354. grub_error_push ();
  355. grub_dprintf ("disk", "Read out of range: sector 0x%llx (%s).\n",
  356. (unsigned long long) sector, grub_errmsg);
  357. grub_error_pop ();
  358. return grub_errno;
  359. }
  360. /* First read until first cache boundary. */
  361. if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1)))
  362. {
  363. grub_disk_addr_t start_sector;
  364. grub_size_t pos;
  365. grub_err_t err;
  366. grub_size_t len;
  367. start_sector = sector & ~((grub_disk_addr_t) GRUB_DISK_CACHE_SIZE - 1);
  368. pos = (sector - start_sector) << GRUB_DISK_SECTOR_BITS;
  369. len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)
  370. - pos - offset);
  371. if (len > size)
  372. len = size;
  373. err = grub_disk_read_small (disk, start_sector,
  374. offset + pos, len, buf);
  375. if (err)
  376. return err;
  377. buf = (char *) buf + len;
  378. size -= len;
  379. offset += len;
  380. sector += (offset >> GRUB_DISK_SECTOR_BITS);
  381. offset &= ((1 << GRUB_DISK_SECTOR_BITS) - 1);
  382. }
  383. /* Until SIZE is zero... */
  384. while (size >= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS))
  385. {
  386. char *data = NULL;
  387. grub_disk_addr_t agglomerate;
  388. grub_err_t err;
  389. /* agglomerate read until we find a first cached entry. */
  390. for (agglomerate = 0; agglomerate
  391. < (size >> (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS))
  392. && agglomerate < disk->max_agglomerate;
  393. agglomerate++)
  394. {
  395. data = grub_disk_cache_fetch (disk->dev->id, disk->id,
  396. sector + (agglomerate
  397. << GRUB_DISK_CACHE_BITS));
  398. if (data)
  399. break;
  400. }
  401. if (data)
  402. {
  403. grub_memcpy ((char *) buf
  404. + (agglomerate << (GRUB_DISK_CACHE_BITS
  405. + GRUB_DISK_SECTOR_BITS)),
  406. data, GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
  407. grub_disk_cache_unlock (disk->dev->id, disk->id,
  408. sector + (agglomerate
  409. << GRUB_DISK_CACHE_BITS));
  410. }
  411. if (agglomerate)
  412. {
  413. grub_disk_addr_t i;
  414. err = (disk->dev->disk_read) (disk, grub_disk_to_native_sector (disk, sector),
  415. agglomerate << (GRUB_DISK_CACHE_BITS
  416. + GRUB_DISK_SECTOR_BITS
  417. - disk->log_sector_size),
  418. buf);
  419. if (err)
  420. return err;
  421. for (i = 0; i < agglomerate; i ++)
  422. grub_disk_cache_store (disk->dev->id, disk->id,
  423. sector + (i << GRUB_DISK_CACHE_BITS),
  424. (char *) buf
  425. + (i << (GRUB_DISK_CACHE_BITS
  426. + GRUB_DISK_SECTOR_BITS)));
  427. if (disk->read_hook)
  428. (disk->read_hook) (sector, 0, agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS),
  429. buf, disk->read_hook_data);
  430. sector += agglomerate << GRUB_DISK_CACHE_BITS;
  431. size -= agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS);
  432. buf = (char *) buf
  433. + (agglomerate << (GRUB_DISK_CACHE_BITS + GRUB_DISK_SECTOR_BITS));
  434. }
  435. if (data)
  436. {
  437. if (disk->read_hook)
  438. (disk->read_hook) (sector, 0, (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS),
  439. buf, disk->read_hook_data);
  440. sector += GRUB_DISK_CACHE_SIZE;
  441. buf = (char *) buf + (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
  442. size -= (GRUB_DISK_CACHE_SIZE << GRUB_DISK_SECTOR_BITS);
  443. }
  444. }
  445. /* And now read the last part. */
  446. if (size)
  447. {
  448. grub_err_t err;
  449. err = grub_disk_read_small (disk, sector, 0, size, buf);
  450. if (err)
  451. return err;
  452. }
  453. return grub_errno;
  454. }
  455. grub_uint64_t
  456. grub_disk_native_sectors (grub_disk_t disk)
  457. {
  458. if (disk->partition)
  459. return grub_partition_get_len (disk->partition);
  460. else if (disk->total_sectors != GRUB_DISK_SIZE_UNKNOWN)
  461. return disk->total_sectors << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
  462. else
  463. return GRUB_DISK_SIZE_UNKNOWN;
  464. }