1 /* pc.c - Read PC style partition tables. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2002,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/partition.h>
21 #include <grub/msdos_partition.h>
22 #include <grub/disk.h>
24 #include <grub/misc.h>
26 #include <grub/i18n.h>
28 GRUB_MOD_LICENSE ("GPLv3+");
30 static struct grub_partition_map grub_msdos_partition_map
;
34 #include <grub/emu/misc.h>
36 struct embed_signature
39 const char *signature
;
41 enum { TYPE_SOFTWARE
, TYPE_RAID
} type
;
44 const char message_warn
[][200] = {
45 /* TRANSLATORS: MBR gap and boot track is the same thing and is the space
46 between MBR and first partitition. If your language translates well only
47 "boot track", you can just use it everywhere. Next two messages are about
48 RAID controllers/software bugs which GRUB has to live with. Please spread
49 the message that these are bugs in other software and not merely
50 suboptimal behaviour. */
51 [TYPE_RAID
] = N_("Sector %llu is already in use by raid controller `%s';"
53 "Please ask the manufacturer not to store data in MBR gap"),
54 [TYPE_SOFTWARE
] = N_("Sector %llu is already in use by the program `%s';"
56 "This software may cause boot or other problems in "
57 "future. Please ask its authors not to store data "
62 /* Signatures of other software that may be using sectors in the embedding
64 struct embed_signature embed_signatures
[] =
74 .signature
= "\xd4\x41\xa0\xf5\x03\x00\x03\x00",
80 .signature
= "\xd8\x41\xa0\xf5\x02\x00\x02\x00",
85 /* from Ryan Perkins */
86 .name
= "HP Backup and Recovery Manager (?)",
87 .signature
= "\x70\x8a\x5d\x46\x35\xc5\x1b\x93"
88 "\xae\x3d\x86\xfd\xb1\x55\x3e\xe0",
93 .name
= "HighPoint RAID controller",
99 /* https://bugs.launchpad.net/bugs/987022 */
100 .name
= "Acer registration utility (?)",
101 .signature
= "GREGRegDone.Tag\x00",
103 .type
= TYPE_SOFTWARE
109 grub_partition_msdos_iterate (grub_disk_t disk
,
110 grub_partition_iterate_hook_t hook
,
113 struct grub_partition p
;
114 struct grub_msdos_partition_mbr mbr
;
116 grub_disk_addr_t lastaddr
;
117 grub_disk_addr_t ext_offset
;
118 grub_disk_addr_t delta
= 0;
120 if (disk
->partition
&& disk
->partition
->partmap
== &grub_msdos_partition_map
)
122 if (disk
->partition
->msdostype
== GRUB_PC_PARTITION_TYPE_LINUX_MINIX
)
123 delta
= disk
->partition
->start
;
125 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "no embedding supported");
131 p
.partmap
= &grub_msdos_partition_map
;
133 /* Any value different than `p.offset' will satisfy the check during
135 lastaddr
= !p
.offset
;
140 struct grub_msdos_partition_entry
*e
;
143 if (grub_disk_read (disk
, p
.offset
, 0, sizeof (mbr
), &mbr
))
146 /* If this is a GPT partition, this MBR is just a dummy. */
148 for (i
= 0; i
< 4; i
++)
149 if (mbr
.entries
[i
].type
== GRUB_PC_PARTITION_TYPE_GPT_DISK
)
150 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "dummy mbr");
152 /* This is our loop-detection algorithm. It works the following way:
153 It saves last position which was a power of two. Then it compares the
154 saved value with a current one. This way it's guaranteed that the loop
155 will be broken by at most third walk.
157 if (labeln
&& lastaddr
== p
.offset
)
158 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "loop detected");
161 if ((labeln
& (labeln
- 1)) == 0)
164 /* Check if it is valid. */
165 if (mbr
.signature
!= grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE
))
166 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "no signature");
168 for (i
= 0; i
< 4; i
++)
169 if (mbr
.entries
[i
].flag
& 0x7f)
170 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "bad boot flag");
172 /* Analyze DOS partitions. */
173 for (p
.index
= 0; p
.index
< 4; p
.index
++)
175 e
= mbr
.entries
+ p
.index
;
178 + (grub_le_to_cpu32 (e
->start
)
179 << (disk
->log_sector_size
- GRUB_DISK_SECTOR_BITS
)) - delta
;
180 p
.len
= grub_le_to_cpu32 (e
->length
)
181 << (disk
->log_sector_size
- GRUB_DISK_SECTOR_BITS
);
182 p
.msdostype
= e
->type
;
184 grub_dprintf ("partition",
185 "partition %d: flag 0x%x, type 0x%x, start 0x%llx, len 0x%llx\n",
186 p
.index
, e
->flag
, e
->type
,
187 (unsigned long long) p
.start
,
188 (unsigned long long) p
.len
);
190 /* If this partition is a normal one, call the hook. */
191 if (! grub_msdos_partition_is_empty (e
->type
)
192 && ! grub_msdos_partition_is_extended (e
->type
))
196 if (hook (disk
, &p
, hook_data
))
199 else if (p
.number
< 3)
200 /* If this partition is a logical one, shouldn't increase the
205 /* Find an extended partition. */
206 for (i
= 0; i
< 4; i
++)
210 if (grub_msdos_partition_is_extended (e
->type
))
212 p
.offset
= ext_offset
213 + (grub_le_to_cpu32 (e
->start
)
214 << (disk
->log_sector_size
- GRUB_DISK_SECTOR_BITS
));
216 ext_offset
= p
.offset
;
222 /* If no extended partition, the end. */
233 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
236 pc_partition_map_embed (struct grub_disk
*disk
, unsigned int *nsectors
,
237 unsigned int max_nsectors
,
238 grub_embed_type_t embed_type
,
239 grub_disk_addr_t
**sectors
)
241 grub_disk_addr_t end
= ~0ULL;
242 struct grub_msdos_partition_mbr mbr
;
244 /* Any value different than `p.offset' will satisfy the check during
246 grub_disk_addr_t lastaddr
= 1;
247 grub_disk_addr_t ext_offset
= 0;
248 grub_disk_addr_t offset
= 0;
250 if (embed_type
!= GRUB_EMBED_PCBIOS
)
251 return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET
,
252 "PC-style partitions curently support "
253 "only PC-BIOS embedding");
256 return grub_error (GRUB_ERR_OUT_OF_RANGE
,
257 "Embedding on MSDOS subpartition isn't supported");
262 struct grub_msdos_partition_entry
*e
;
266 err
= grub_disk_read (disk
, offset
, 0, sizeof (mbr
), &mbr
);
270 /* This is our loop-detection algorithm. It works the following way:
271 It saves last position which was a power of two. Then it compares the
272 saved value with a current one. This way it's guaranteed that the loop
273 will be broken by at most third walk.
275 if (labeln
&& lastaddr
== offset
)
276 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "loop detected");
279 if ((labeln
& (labeln
- 1)) == 0)
282 /* Check if it is valid. */
283 if (mbr
.signature
!= grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE
))
284 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "no signature");
286 for (i
= 0; i
< 4; i
++)
287 if (mbr
.entries
[i
].flag
& 0x7f)
288 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "bad boot flag");
290 /* Analyze DOS partitions. */
291 for (i
= 0; i
< 4; i
++)
295 if (!grub_msdos_partition_is_empty (e
->type
)
297 + (grub_le_to_cpu32 (e
->start
)
298 << (disk
->log_sector_size
- GRUB_DISK_SECTOR_BITS
)))
299 end
= offset
+ (grub_le_to_cpu32 (e
->start
)
300 << (disk
->log_sector_size
- GRUB_DISK_SECTOR_BITS
));
302 /* If this is a GPT partition, this MBR is just a dummy. */
303 if (e
->type
== GRUB_PC_PARTITION_TYPE_GPT_DISK
&& i
== 0)
304 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "dummy mbr");
307 /* Find an extended partition. */
308 for (i
= 0; i
< 4; i
++)
312 if (grub_msdos_partition_is_extended (e
->type
))
315 + (grub_le_to_cpu32 (e
->start
)
316 << (disk
->log_sector_size
- GRUB_DISK_SECTOR_BITS
));
324 /* If no extended partition, the end. */
329 if (end
>= *nsectors
+ 1)
332 char *embed_signature_check
;
333 unsigned int orig_nsectors
, avail_nsectors
;
335 orig_nsectors
= *nsectors
;
337 avail_nsectors
= *nsectors
;
338 if (*nsectors
> max_nsectors
)
339 *nsectors
= max_nsectors
;
340 *sectors
= grub_malloc (*nsectors
* sizeof (**sectors
));
343 for (i
= 0; i
< *nsectors
; i
++)
344 (*sectors
)[i
] = 1 + i
;
346 /* Check for software that is already using parts of the embedding
349 embed_signature_check
= grub_malloc (GRUB_DISK_SECTOR_SIZE
);
350 for (i
= 0; i
< *nsectors
; i
++)
352 if (grub_disk_read (disk
, (*sectors
)[i
], 0, GRUB_DISK_SECTOR_SIZE
,
353 embed_signature_check
))
356 for (j
= 0; j
< ARRAY_SIZE (embed_signatures
); j
++)
357 if (! grub_memcmp (embed_signatures
[j
].signature
,
358 embed_signature_check
,
359 embed_signatures
[j
].signature_len
))
361 if (j
== ARRAY_SIZE (embed_signatures
))
363 grub_util_warn (_(message_warn
[embed_signatures
[j
].type
]),
364 (*sectors
)[i
], embed_signatures
[j
].name
);
366 if (avail_nsectors
< *nsectors
)
367 *nsectors
= avail_nsectors
;
369 /* Avoid this sector. */
370 for (j
= i
; j
< *nsectors
; j
++)
373 /* Have we run out of space? */
374 if (avail_nsectors
< orig_nsectors
)
377 /* Make sure to check the next sector. */
380 grub_free (embed_signature_check
);
382 if (*nsectors
< orig_nsectors
)
383 return grub_error (GRUB_ERR_OUT_OF_RANGE
,
384 N_("other software is using the embedding area, and "
385 "there is not enough room for core.img. Such "
386 "software is often trying to store data in a way "
387 "that avoids detection. We recommend you "
390 return GRUB_ERR_NONE
;
394 return grub_error (GRUB_ERR_FILE_NOT_FOUND
,
395 N_("this msdos-style partition label has no "
396 "post-MBR gap; embedding won't be possible"));
399 return grub_error (GRUB_ERR_OUT_OF_RANGE
,
400 N_("your core.img is unusually large. "
401 "It won't fit in the embedding area"));
403 return grub_error (GRUB_ERR_OUT_OF_RANGE
,
404 N_("your embedding area is unusually small. "
405 "core.img won't fit in it."));
408 #pragma GCC diagnostic error "-Wformat-nonliteral"
413 /* Partition map type. */
414 static struct grub_partition_map grub_msdos_partition_map
=
417 .iterate
= grub_partition_msdos_iterate
,
419 .embed
= pc_partition_map_embed
423 GRUB_MOD_INIT(part_msdos
)
425 grub_partition_map_register (&grub_msdos_partition_map
);
428 GRUB_MOD_FINI(part_msdos
)
430 grub_partition_map_unregister (&grub_msdos_partition_map
);