msdos.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. /* pc.c - Read PC style partition tables. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2002,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
  5. *
  6. * GRUB is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/partition.h>
  20. #include <grub/msdos_partition.h>
  21. #include <grub/disk.h>
  22. #include <grub/mm.h>
  23. #include <grub/misc.h>
  24. #include <grub/dl.h>
  25. #include <grub/i18n.h>
  26. GRUB_MOD_LICENSE ("GPLv3+");
  27. static struct grub_partition_map grub_msdos_partition_map;
  28. #ifdef GRUB_UTIL
  29. #include <grub/emu/misc.h>
  30. struct embed_signature
  31. {
  32. const char *name;
  33. const char *signature;
  34. int signature_len;
  35. enum { TYPE_SOFTWARE, TYPE_RAID } type;
  36. };
  37. const char message_warn[][200] = {
  38. /* TRANSLATORS: MBR gap and boot track is the same thing and is the space
  39. between MBR and first partitition. If your language translates well only
  40. "boot track", you can just use it everywhere. Next two messages are about
  41. RAID controllers/software bugs which GRUB has to live with. Please spread
  42. the message that these are bugs in other software and not merely
  43. suboptimal behaviour. */
  44. [TYPE_RAID] = N_("Sector %llu is already in use by raid controller `%s';"
  45. " avoiding it. "
  46. "Please ask the manufacturer not to store data in MBR gap"),
  47. [TYPE_SOFTWARE] = N_("Sector %llu is already in use by the program `%s';"
  48. " avoiding it. "
  49. "This software may cause boot or other problems in "
  50. "future. Please ask its authors not to store data "
  51. "in the boot track")
  52. };
  53. /* Signatures of other software that may be using sectors in the embedding
  54. area. */
  55. struct embed_signature embed_signatures[] =
  56. {
  57. {
  58. .name = "ZISD",
  59. .signature = "ZISD",
  60. .signature_len = 4,
  61. .type = TYPE_SOFTWARE
  62. },
  63. {
  64. .name = "FlexNet",
  65. .signature = "\xd4\x41\xa0\xf5\x03\x00\x03\x00",
  66. .signature_len = 8,
  67. .type = TYPE_SOFTWARE
  68. },
  69. {
  70. .name = "FlexNet",
  71. .signature = "\xd8\x41\xa0\xf5\x02\x00\x02\x00",
  72. .signature_len = 8,
  73. .type = TYPE_SOFTWARE
  74. },
  75. {
  76. /* from Ryan Perkins */
  77. .name = "HP Backup and Recovery Manager (?)",
  78. .signature = "\x70\x8a\x5d\x46\x35\xc5\x1b\x93"
  79. "\xae\x3d\x86\xfd\xb1\x55\x3e\xe0",
  80. .signature_len = 16,
  81. .type = TYPE_SOFTWARE
  82. },
  83. {
  84. .name = "HighPoint RAID controller",
  85. .signature = "ycgl",
  86. .signature_len = 4,
  87. .type = TYPE_RAID
  88. },
  89. {
  90. /* https://bugs.launchpad.net/bugs/987022 */
  91. .name = "Acer registration utility (?)",
  92. .signature = "GREGRegDone.Tag\x00",
  93. .signature_len = 16,
  94. .type = TYPE_SOFTWARE
  95. }
  96. };
  97. #endif
  98. grub_err_t
  99. grub_partition_msdos_iterate (grub_disk_t disk,
  100. grub_partition_iterate_hook_t hook,
  101. void *hook_data)
  102. {
  103. struct grub_partition p;
  104. struct grub_msdos_partition_mbr mbr;
  105. int labeln = 0;
  106. grub_disk_addr_t lastaddr;
  107. grub_disk_addr_t ext_offset;
  108. grub_disk_addr_t delta = 0;
  109. if (disk->partition && disk->partition->partmap == &grub_msdos_partition_map)
  110. {
  111. if (disk->partition->msdostype == GRUB_PC_PARTITION_TYPE_LINUX_MINIX)
  112. delta = disk->partition->start;
  113. else
  114. return grub_error (GRUB_ERR_BAD_PART_TABLE, "no embedding supported");
  115. }
  116. p.offset = 0;
  117. ext_offset = 0;
  118. p.number = -1;
  119. p.partmap = &grub_msdos_partition_map;
  120. /* Any value different than `p.offset' will satisfy the check during
  121. first loop. */
  122. lastaddr = !p.offset;
  123. while (1)
  124. {
  125. int i;
  126. struct grub_msdos_partition_entry *e;
  127. /* Read the MBR. */
  128. if (grub_disk_read (disk, p.offset, 0, sizeof (mbr), &mbr))
  129. goto finish;
  130. /* If this is a GPT partition, this MBR is just a dummy. */
  131. if (p.offset == 0)
  132. for (i = 0; i < 4; i++)
  133. if (mbr.entries[i].type == GRUB_PC_PARTITION_TYPE_GPT_DISK)
  134. return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr");
  135. /* This is our loop-detection algorithm. It works the following way:
  136. It saves last position which was a power of two. Then it compares the
  137. saved value with a current one. This way it's guaranteed that the loop
  138. will be broken by at most third walk.
  139. */
  140. if (labeln && lastaddr == p.offset)
  141. return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected");
  142. labeln++;
  143. if ((labeln & (labeln - 1)) == 0)
  144. lastaddr = p.offset;
  145. /* Check if it is valid. */
  146. if (mbr.signature != grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE))
  147. return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");
  148. for (i = 0; i < 4; i++)
  149. if (mbr.entries[i].flag & 0x7f)
  150. return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag");
  151. /* Analyze DOS partitions. */
  152. for (p.index = 0; p.index < 4; p.index++)
  153. {
  154. e = mbr.entries + p.index;
  155. p.start = p.offset
  156. + ((grub_disk_addr_t)grub_le_to_cpu32 (e->start)
  157. << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)) - delta;
  158. p.len = (grub_uint64_t)grub_le_to_cpu32 (e->length)
  159. << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS);
  160. p.msdostype = e->type;
  161. grub_dprintf ("partition",
  162. "partition %d: flag 0x%x, type 0x%x, start 0x%llx, len 0x%llx\n",
  163. p.index, e->flag, e->type,
  164. (unsigned long long) p.start,
  165. (unsigned long long) p.len);
  166. /* If this partition is a normal one, call the hook. */
  167. if (! grub_msdos_partition_is_empty (e->type)
  168. && ! grub_msdos_partition_is_extended (e->type))
  169. {
  170. p.number++;
  171. if (hook (disk, &p, hook_data))
  172. return grub_errno;
  173. }
  174. else if (p.number < 3)
  175. /* If this partition is a logical one, shouldn't increase the
  176. partition number. */
  177. p.number++;
  178. }
  179. /* Find an extended partition. */
  180. for (i = 0; i < 4; i++)
  181. {
  182. e = mbr.entries + i;
  183. if (grub_msdos_partition_is_extended (e->type))
  184. {
  185. p.offset = ext_offset
  186. + ((grub_disk_addr_t)grub_le_to_cpu32 (e->start)
  187. << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS));
  188. if (! ext_offset)
  189. ext_offset = p.offset;
  190. break;
  191. }
  192. }
  193. /* If no extended partition, the end. */
  194. if (i == 4)
  195. break;
  196. }
  197. finish:
  198. return grub_errno;
  199. }
  200. #ifdef GRUB_UTIL
  201. #pragma GCC diagnostic ignored "-Wformat-nonliteral"
  202. static grub_err_t
  203. pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
  204. unsigned int max_nsectors,
  205. grub_embed_type_t embed_type,
  206. grub_disk_addr_t **sectors,
  207. int warn_short)
  208. {
  209. grub_disk_addr_t end = ~0ULL;
  210. struct grub_msdos_partition_mbr mbr;
  211. int labeln = 0;
  212. /* Any value different than `p.offset' will satisfy the check during
  213. first loop. */
  214. grub_disk_addr_t lastaddr = 1;
  215. grub_disk_addr_t ext_offset = 0;
  216. grub_disk_addr_t offset = 0;
  217. if (embed_type != GRUB_EMBED_PCBIOS)
  218. return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
  219. "PC-style partitions currently support "
  220. "only PC-BIOS embedding");
  221. if (disk->partition)
  222. return grub_error (GRUB_ERR_OUT_OF_RANGE,
  223. "Embedding on MSDOS subpartition isn't supported");
  224. while (1)
  225. {
  226. int i;
  227. struct grub_msdos_partition_entry *e;
  228. grub_err_t err;
  229. /* Read the MBR. */
  230. err = grub_disk_read (disk, offset, 0, sizeof (mbr), &mbr);
  231. if (err)
  232. return err;
  233. /* This is our loop-detection algorithm. It works the following way:
  234. It saves last position which was a power of two. Then it compares the
  235. saved value with a current one. This way it's guaranteed that the loop
  236. will be broken by at most third walk.
  237. */
  238. if (labeln && lastaddr == offset)
  239. return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected");
  240. labeln++;
  241. if ((labeln & (labeln - 1)) == 0)
  242. lastaddr = offset;
  243. /* Check if it is valid. */
  244. if (mbr.signature != grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE))
  245. return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");
  246. for (i = 0; i < 4; i++)
  247. if (mbr.entries[i].flag & 0x7f)
  248. return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag");
  249. /* Analyze DOS partitions. */
  250. for (i = 0; i < 4; i++)
  251. {
  252. e = mbr.entries + i;
  253. if (!grub_msdos_partition_is_empty (e->type)
  254. && end > offset
  255. + ((grub_disk_addr_t)grub_le_to_cpu32 (e->start)
  256. << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
  257. end = offset + ((grub_disk_addr_t)grub_le_to_cpu32 (e->start)
  258. << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS));
  259. /* If this is a GPT partition, this MBR is just a dummy. */
  260. if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && i == 0)
  261. return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr");
  262. }
  263. /* Find an extended partition. */
  264. for (i = 0; i < 4; i++)
  265. {
  266. e = mbr.entries + i;
  267. if (grub_msdos_partition_is_extended (e->type))
  268. {
  269. offset = ext_offset
  270. + ((grub_disk_addr_t)grub_le_to_cpu32 (e->start)
  271. << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS));
  272. if (! ext_offset)
  273. ext_offset = offset;
  274. break;
  275. }
  276. }
  277. /* If no extended partition, the end. */
  278. if (i == 4)
  279. break;
  280. }
  281. if (end >= *nsectors + 1)
  282. {
  283. unsigned i, j;
  284. char *embed_signature_check;
  285. unsigned int orig_nsectors, avail_nsectors;
  286. orig_nsectors = *nsectors;
  287. *nsectors = end - 1;
  288. avail_nsectors = *nsectors;
  289. if (*nsectors > max_nsectors)
  290. *nsectors = max_nsectors;
  291. *sectors = grub_calloc (*nsectors, sizeof (**sectors));
  292. if (!*sectors)
  293. return grub_errno;
  294. for (i = 0; i < *nsectors; i++)
  295. (*sectors)[i] = 1 + i;
  296. /* Check for software that is already using parts of the embedding
  297. * area.
  298. */
  299. embed_signature_check = grub_malloc (GRUB_DISK_SECTOR_SIZE);
  300. for (i = 0; i < *nsectors; i++)
  301. {
  302. if (grub_disk_read (disk, (*sectors)[i], 0, GRUB_DISK_SECTOR_SIZE,
  303. embed_signature_check))
  304. continue;
  305. for (j = 0; j < ARRAY_SIZE (embed_signatures); j++)
  306. if (! grub_memcmp (embed_signatures[j].signature,
  307. embed_signature_check,
  308. embed_signatures[j].signature_len))
  309. break;
  310. if (j == ARRAY_SIZE (embed_signatures))
  311. continue;
  312. grub_util_warn (_(message_warn[embed_signatures[j].type]),
  313. (*sectors)[i], embed_signatures[j].name);
  314. avail_nsectors--;
  315. if (avail_nsectors < *nsectors)
  316. *nsectors = avail_nsectors;
  317. /* Avoid this sector. */
  318. for (j = i; j < *nsectors; j++)
  319. (*sectors)[j]++;
  320. /* Have we run out of space? */
  321. if (avail_nsectors < orig_nsectors)
  322. break;
  323. /* Make sure to check the next sector. */
  324. i--;
  325. }
  326. grub_free (embed_signature_check);
  327. if (*nsectors < orig_nsectors)
  328. return grub_error (GRUB_ERR_OUT_OF_RANGE,
  329. N_("other software is using the embedding area, and "
  330. "there is not enough room for core.img. Such "
  331. "software is often trying to store data in a way "
  332. "that avoids detection. We recommend you "
  333. "investigate"));
  334. return GRUB_ERR_NONE;
  335. }
  336. if (end < GRUB_MIN_RECOMMENDED_MBR_GAP && warn_short)
  337. grub_util_warn ("You have a short MBR gap and use advanced config. Please increase post-MBR gap.");
  338. if (end <= 1)
  339. return grub_error (GRUB_ERR_FILE_NOT_FOUND,
  340. N_("this msdos-style partition label has no "
  341. "post-MBR gap; embedding won't be possible"));
  342. if (*nsectors > 62)
  343. return grub_error (GRUB_ERR_OUT_OF_RANGE,
  344. N_("your core.img is unusually large. "
  345. "It won't fit in the embedding area"));
  346. return grub_error (GRUB_ERR_OUT_OF_RANGE,
  347. N_("your embedding area is unusually small. "
  348. "core.img won't fit in it."));
  349. }
  350. #pragma GCC diagnostic error "-Wformat-nonliteral"
  351. #endif
  352. /* Partition map type. */
  353. static struct grub_partition_map grub_msdos_partition_map =
  354. {
  355. .name = "msdos",
  356. .iterate = grub_partition_msdos_iterate,
  357. #ifdef GRUB_UTIL
  358. .embed = pc_partition_map_embed
  359. #endif
  360. };
  361. GRUB_MOD_INIT(part_msdos)
  362. {
  363. grub_partition_map_register (&grub_msdos_partition_map);
  364. }
  365. GRUB_MOD_FINI(part_msdos)
  366. {
  367. grub_partition_map_unregister (&grub_msdos_partition_map);
  368. }