msdos.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  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. {
  208. grub_disk_addr_t end = ~0ULL;
  209. struct grub_msdos_partition_mbr mbr;
  210. int labeln = 0;
  211. /* Any value different than `p.offset' will satisfy the check during
  212. first loop. */
  213. grub_disk_addr_t lastaddr = 1;
  214. grub_disk_addr_t ext_offset = 0;
  215. grub_disk_addr_t offset = 0;
  216. if (embed_type != GRUB_EMBED_PCBIOS)
  217. return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
  218. "PC-style partitions currently support "
  219. "only PC-BIOS embedding");
  220. if (disk->partition)
  221. return grub_error (GRUB_ERR_OUT_OF_RANGE,
  222. "Embedding on MSDOS subpartition isn't supported");
  223. while (1)
  224. {
  225. int i;
  226. struct grub_msdos_partition_entry *e;
  227. grub_err_t err;
  228. /* Read the MBR. */
  229. err = grub_disk_read (disk, offset, 0, sizeof (mbr), &mbr);
  230. if (err)
  231. return err;
  232. /* This is our loop-detection algorithm. It works the following way:
  233. It saves last position which was a power of two. Then it compares the
  234. saved value with a current one. This way it's guaranteed that the loop
  235. will be broken by at most third walk.
  236. */
  237. if (labeln && lastaddr == offset)
  238. return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected");
  239. labeln++;
  240. if ((labeln & (labeln - 1)) == 0)
  241. lastaddr = offset;
  242. /* Check if it is valid. */
  243. if (mbr.signature != grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE))
  244. return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");
  245. for (i = 0; i < 4; i++)
  246. if (mbr.entries[i].flag & 0x7f)
  247. return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag");
  248. /* Analyze DOS partitions. */
  249. for (i = 0; i < 4; i++)
  250. {
  251. e = mbr.entries + i;
  252. if (!grub_msdos_partition_is_empty (e->type)
  253. && end > offset
  254. + ((grub_disk_addr_t)grub_le_to_cpu32 (e->start)
  255. << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS)))
  256. end = offset + ((grub_disk_addr_t)grub_le_to_cpu32 (e->start)
  257. << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS));
  258. /* If this is a GPT partition, this MBR is just a dummy. */
  259. if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && i == 0)
  260. return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr");
  261. }
  262. /* Find an extended partition. */
  263. for (i = 0; i < 4; i++)
  264. {
  265. e = mbr.entries + i;
  266. if (grub_msdos_partition_is_extended (e->type))
  267. {
  268. offset = ext_offset
  269. + ((grub_disk_addr_t)grub_le_to_cpu32 (e->start)
  270. << (disk->log_sector_size - GRUB_DISK_SECTOR_BITS));
  271. if (! ext_offset)
  272. ext_offset = offset;
  273. break;
  274. }
  275. }
  276. /* If no extended partition, the end. */
  277. if (i == 4)
  278. break;
  279. }
  280. if (end >= *nsectors + 1)
  281. {
  282. unsigned i, j;
  283. char *embed_signature_check;
  284. unsigned int orig_nsectors, avail_nsectors;
  285. orig_nsectors = *nsectors;
  286. *nsectors = end - 1;
  287. avail_nsectors = *nsectors;
  288. if (*nsectors > max_nsectors)
  289. *nsectors = max_nsectors;
  290. *sectors = grub_malloc (*nsectors * sizeof (**sectors));
  291. if (!*sectors)
  292. return grub_errno;
  293. for (i = 0; i < *nsectors; i++)
  294. (*sectors)[i] = 1 + i;
  295. /* Check for software that is already using parts of the embedding
  296. * area.
  297. */
  298. embed_signature_check = grub_malloc (GRUB_DISK_SECTOR_SIZE);
  299. for (i = 0; i < *nsectors; i++)
  300. {
  301. if (grub_disk_read (disk, (*sectors)[i], 0, GRUB_DISK_SECTOR_SIZE,
  302. embed_signature_check))
  303. continue;
  304. for (j = 0; j < ARRAY_SIZE (embed_signatures); j++)
  305. if (! grub_memcmp (embed_signatures[j].signature,
  306. embed_signature_check,
  307. embed_signatures[j].signature_len))
  308. break;
  309. if (j == ARRAY_SIZE (embed_signatures))
  310. continue;
  311. grub_util_warn (_(message_warn[embed_signatures[j].type]),
  312. (*sectors)[i], embed_signatures[j].name);
  313. avail_nsectors--;
  314. if (avail_nsectors < *nsectors)
  315. *nsectors = avail_nsectors;
  316. /* Avoid this sector. */
  317. for (j = i; j < *nsectors; j++)
  318. (*sectors)[j]++;
  319. /* Have we run out of space? */
  320. if (avail_nsectors < orig_nsectors)
  321. break;
  322. /* Make sure to check the next sector. */
  323. i--;
  324. }
  325. grub_free (embed_signature_check);
  326. if (*nsectors < orig_nsectors)
  327. return grub_error (GRUB_ERR_OUT_OF_RANGE,
  328. N_("other software is using the embedding area, and "
  329. "there is not enough room for core.img. Such "
  330. "software is often trying to store data in a way "
  331. "that avoids detection. We recommend you "
  332. "investigate"));
  333. return GRUB_ERR_NONE;
  334. }
  335. if (end <= 1)
  336. return grub_error (GRUB_ERR_FILE_NOT_FOUND,
  337. N_("this msdos-style partition label has no "
  338. "post-MBR gap; embedding won't be possible"));
  339. if (*nsectors > 62)
  340. return grub_error (GRUB_ERR_OUT_OF_RANGE,
  341. N_("your core.img is unusually large. "
  342. "It won't fit in the embedding area"));
  343. return grub_error (GRUB_ERR_OUT_OF_RANGE,
  344. N_("your embedding area is unusually small. "
  345. "core.img won't fit in it."));
  346. }
  347. #pragma GCC diagnostic error "-Wformat-nonliteral"
  348. #endif
  349. /* Partition map type. */
  350. static struct grub_partition_map grub_msdos_partition_map =
  351. {
  352. .name = "msdos",
  353. .iterate = grub_partition_msdos_iterate,
  354. #ifdef GRUB_UTIL
  355. .embed = pc_partition_map_embed
  356. #endif
  357. };
  358. GRUB_MOD_INIT(part_msdos)
  359. {
  360. grub_partition_map_register (&grub_msdos_partition_map);
  361. }
  362. GRUB_MOD_FINI(part_msdos)
  363. {
  364. grub_partition_map_unregister (&grub_msdos_partition_map);
  365. }