2 * fs/partitions/msdos.c
4 * Code extracted from drivers/block/genhd.c
5 * Copyright (C) 1991-1998 Linus Torvalds
7 * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
8 * in the early extended-partition checks and added DM partitions
10 * Support for DiskManager v6.0x added by Mark Lord,
11 * with information provided by OnTrack. This now works for linux fdisk
12 * and LILO, as well as loadlin and bootln. Note that disks other than
13 * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
15 * More flexible handling of extended partitions - aeb, 950831
17 * Check partition table on IDE disks for common CHS translations
19 * Re-organised Feb 1998 Russell King
22 #include <linux/config.h>
24 #include <linux/genhd.h>
25 #include <linux/kernel.h>
26 #include <linux/major.h>
27 #include <linux/string.h>
28 #include <linux/blk.h>
30 #ifdef CONFIG_BLK_DEV_IDE
31 #include <linux/ide.h> /* IDE xlate */
32 #endif /* CONFIG_BLK_DEV_IDE */
34 #include <asm/system.h>
39 static int current_minor
;
42 * Many architectures don't like unaligned accesses, which is
43 * frequently the case with the nr_sects and start_sect partition
46 #include <asm/unaligned.h>
48 #define SYS_IND(p) (get_unaligned(&p->sys_ind))
49 #define NR_SECTS(p) ({ __typeof__(p->nr_sects) __a = \
50 get_unaligned(&p->nr_sects); \
54 #define START_SECT(p) ({ __typeof__(p->start_sect) __a = \
55 get_unaligned(&p->start_sect); \
59 static inline int is_extended_partition(struct partition
*p
)
61 return (SYS_IND(p
) == DOS_EXTENDED_PARTITION
||
62 SYS_IND(p
) == WIN98_EXTENDED_PARTITION
||
63 SYS_IND(p
) == LINUX_EXTENDED_PARTITION
);
67 * Create devices for each logical partition in an extended partition.
68 * The logical partitions form a linked list, with each entry being
69 * a partition table with two entries. The first entry
70 * is the real data partition (with a start relative to the partition
71 * table start). The second is a pointer to the next logical partition
72 * (with a start relative to the entire extended partition).
73 * We do not create a Linux partition for the partition tables, but
74 * only for the actual data partitions.
77 static void extended_partition(struct gendisk
*hd
, kdev_t dev
)
79 struct buffer_head
*bh
;
81 unsigned long first_sector
, first_size
, this_sector
, this_size
;
82 int mask
= (1 << hd
->minor_shift
) - 1;
83 int sector_size
= get_hardsect_size(dev
) / 512;
84 int loopct
= 0; /* number of links followed
85 without finding a data partition */
88 first_sector
= hd
->part
[MINOR(dev
)].start_sect
;
89 first_size
= hd
->part
[MINOR(dev
)].nr_sects
;
90 this_sector
= first_sector
;
95 if ((current_minor
& mask
) == 0)
97 if (!(bh
= bread(dev
,0,get_ptable_blocksize(dev
))))
100 if ((*(__u16
*) (bh
->b_data
+510)) != cpu_to_le16(MSDOS_LABEL_MAGIC
))
103 p
= (struct partition
*) (0x1BE + bh
->b_data
);
105 this_size
= hd
->part
[MINOR(dev
)].nr_sects
;
108 * Usually, the first entry is the real data partition,
109 * the 2nd entry is the next extended partition, or empty,
110 * and the 3rd and 4th entries are unused.
111 * However, DRDOS sometimes has the extended partition as
112 * the first entry (when the data partition is empty),
113 * and OS/2 seems to use all four entries.
117 * First process the data partition(s)
119 for (i
=0; i
<4; i
++, p
++) {
120 if (!NR_SECTS(p
) || is_extended_partition(p
))
123 /* Check the 3rd and 4th entries -
124 these sometimes contain random garbage */
126 && START_SECT(p
) + NR_SECTS(p
) > this_size
127 && (this_sector
+ START_SECT(p
) < first_sector
||
128 this_sector
+ START_SECT(p
) + NR_SECTS(p
) >
129 first_sector
+ first_size
))
132 add_gd_partition(hd
, current_minor
,
133 this_sector
+START_SECT(p
)*sector_size
,
134 NR_SECTS(p
)*sector_size
);
137 if ((current_minor
& mask
) == 0)
141 * Next, process the (first) extended partition, if present.
142 * (So far, there seems to be no reason to make
143 * extended_partition() recursive and allow a tree
144 * of extended partitions.)
145 * It should be a link to the next logical partition.
146 * Create a minor for this just long enough to get the next
147 * partition table. The minor will be reused for the next
151 for (i
=0; i
<4; i
++, p
++)
152 if(NR_SECTS(p
) && is_extended_partition(p
))
155 goto done
; /* nothing left to do */
157 hd
->part
[current_minor
].nr_sects
= NR_SECTS(p
) * sector_size
; /* JSt */
158 hd
->part
[current_minor
].start_sect
= first_sector
+ START_SECT(p
) * sector_size
;
159 this_sector
= first_sector
+ START_SECT(p
) * sector_size
;
160 dev
= MKDEV(hd
->major
, current_minor
);
162 /* Use bforget(), as we have changed the disk geometry */
169 static inline struct buffer_head
*
170 get_partition_table_block(struct gendisk
*hd
, int minor
, int blocknr
) {
171 kdev_t dev
= MKDEV(hd
->major
, minor
);
172 return bread(dev
, blocknr
, get_ptable_blocksize(dev
));
175 #ifdef CONFIG_SOLARIS_X86_PARTITION
177 /* james@bpgc.com: Solaris has a nasty indicator: 0x82 which also
178 indicates linux swap. Be careful before believing this is Solaris. */
181 solaris_x86_partition(struct gendisk
*hd
, int minor
) {
182 long offset
= hd
->part
[minor
].start_sect
;
184 struct buffer_head
*bh
;
185 struct solaris_x86_vtoc
*v
;
186 struct solaris_x86_slice
*s
;
190 if(!(bh
= get_partition_table_block(hd
, minor
, 0)))
192 v
= (struct solaris_x86_vtoc
*)(bh
->b_data
+ 512);
193 if(v
->v_sanity
!= SOLARIS_X86_VTOC_SANE
) {
197 printk(" %s: <solaris:", disk_name(hd
, minor
, buf
));
198 if(v
->v_version
!= 1) {
199 printk(" cannot handle version %ld vtoc>\n", v
->v_version
);
203 for(i
=0; i
<SOLARIS_X86_NUMSLICE
; i
++) {
209 /* solaris partitions are relative to current MS-DOS
210 * one but add_gd_partition starts relative to sector
211 * zero of the disk. Therefore, must add the offset
212 * of the current partition */
213 add_gd_partition(hd
, current_minor
, s
->s_start
+offset
, s
->s_size
);
221 #ifdef CONFIG_BSD_DISKLABEL
223 check_and_add_bsd_partition(struct gendisk
*hd
,
224 struct bsd_partition
*bsd_p
, int minor
) {
225 struct hd_struct
*lin_p
;
226 /* check relative position of partitions. */
227 for (lin_p
= hd
->part
+ 1 + minor
;
228 lin_p
- hd
->part
- minor
< current_minor
; lin_p
++) {
229 /* no relationship -> try again */
230 if (lin_p
->start_sect
+ lin_p
->nr_sects
<= bsd_p
->p_offset
||
231 lin_p
->start_sect
>= bsd_p
->p_offset
+ bsd_p
->p_size
)
233 /* equal -> no need to add */
234 if (lin_p
->start_sect
== bsd_p
->p_offset
&&
235 lin_p
->nr_sects
== bsd_p
->p_size
)
237 /* bsd living within dos partition */
238 if (lin_p
->start_sect
<= bsd_p
->p_offset
&& lin_p
->start_sect
239 + lin_p
->nr_sects
>= bsd_p
->p_offset
+ bsd_p
->p_size
) {
240 #ifdef DEBUG_BSD_DISKLABEL
241 printk("w: %d %ld+%ld,%d+%d",
243 lin_p
->start_sect
, lin_p
->nr_sects
,
244 bsd_p
->p_offset
, bsd_p
->p_size
);
248 /* ouch: bsd and linux overlap. Don't even try for that partition */
249 #ifdef DEBUG_BSD_DISKLABEL
250 printk("???: %d %ld+%ld,%d+%d",
251 lin_p
- hd
->part
, lin_p
->start_sect
, lin_p
->nr_sects
,
252 bsd_p
->p_offset
, bsd_p
->p_size
);
256 } /* if the bsd partition is not currently known to linux, we end
259 add_gd_partition(hd
, current_minor
, bsd_p
->p_offset
, bsd_p
->p_size
);
264 * Create devices for BSD partitions listed in a disklabel, under a
265 * dos-like partition. See extended_partition() for more information.
267 static void bsd_disklabel_partition(struct gendisk
*hd
, int minor
, int type
) {
268 struct buffer_head
*bh
;
269 struct bsd_disklabel
*l
;
270 struct bsd_partition
*p
;
272 int mask
= (1 << hd
->minor_shift
) - 1;
275 if (!(bh
= get_partition_table_block(hd
, minor
, 0)))
277 l
= (struct bsd_disklabel
*) (bh
->b_data
+512);
278 if (l
->d_magic
!= BSD_DISKMAGIC
) {
282 printk(" %s:", disk_name(hd
, minor
, buf
));
283 printk((type
== OPENBSD_PARTITION
) ? " <openbsd:" :
284 (type
== NETBSD_PARTITION
) ? " <netbsd:" : " <bsd:");
286 max_partitions
= ((type
== OPENBSD_PARTITION
) ? OPENBSD_MAXPARTITIONS
287 : BSD_MAXPARTITIONS
);
288 if (l
->d_npartitions
< max_partitions
)
289 max_partitions
= l
->d_npartitions
;
290 for (p
= l
->d_partitions
; p
- l
->d_partitions
< max_partitions
; p
++) {
291 if ((current_minor
& mask
) >= (4 + hd
->max_p
))
294 if (p
->p_fstype
!= BSD_FS_UNUSED
)
295 check_and_add_bsd_partition(hd
, p
, minor
);
298 /* Use bforget(), as we have changed the disk setup */
305 #ifdef CONFIG_UNIXWARE_DISKLABEL
307 * Create devices for Unixware partitions listed in a disklabel, under a
308 * dos-like partition. See extended_partition() for more information.
310 static void unixware_partition(struct gendisk
*hd
, int minor
) {
311 struct buffer_head
*bh
;
312 struct unixware_disklabel
*l
;
313 struct unixware_slice
*p
;
314 int mask
= (1 << hd
->minor_shift
) - 1;
317 if (!(bh
= get_partition_table_block(hd
, minor
, 14)))
319 l
= (struct unixware_disklabel
*) (bh
->b_data
+512);
320 if (le32_to_cpu(l
->d_magic
) != UNIXWARE_DISKMAGIC
||
321 le32_to_cpu(l
->vtoc
.v_magic
) != UNIXWARE_DISKMAGIC2
) {
325 printk(" %s: <unixware:", disk_name(hd
, minor
, buf
));
326 p
= &l
->vtoc
.v_slice
[1];
327 /* I omit the 0th slice as it is the same as whole disk. */
328 while (p
- &l
->vtoc
.v_slice
[0] < UNIXWARE_NUMSLICE
) {
329 if ((current_minor
& mask
) == 0)
332 if (p
->s_label
!= UNIXWARE_FS_UNUSED
) {
333 add_gd_partition(hd
, current_minor
, START_SECT(p
),
339 /* Use bforget, as we have changed the disk setup */
345 int msdos_partition(struct gendisk
*hd
, kdev_t dev
,
346 unsigned long first_sector
, int first_part_minor
) {
347 int i
, minor
= current_minor
= first_part_minor
;
348 struct buffer_head
*bh
;
351 int mask
= (1 << hd
->minor_shift
) - 1;
352 int sector_size
= get_hardsect_size(dev
) / 512;
353 #ifdef CONFIG_BLK_DEV_IDE
354 int tested_for_xlate
= 0;
357 #endif /* CONFIG_BLK_DEV_IDE */
358 if (!(bh
= bread(dev
,0,get_ptable_blocksize(dev
)))) {
359 if (warn_no_part
) printk(" unable to read partition table\n");
363 #ifdef CONFIG_BLK_DEV_IDE
365 #endif /* CONFIG_BLK_DEV_IDE */
366 /* Use bforget(), because we may have changed the disk geometry */
367 if (*(unsigned short *) (0x1fe + data
) != cpu_to_le16(MSDOS_LABEL_MAGIC
)) {
371 p
= (struct partition
*) (0x1be + data
);
373 #ifdef CONFIG_BLK_DEV_IDE
374 if (!tested_for_xlate
++) { /* Do this only once per disk */
376 * Look for various forms of IDE disk geometry translation
378 unsigned int sig
= le16_to_cpu(*(unsigned short *)(data
+ 2));
381 * The i386 partition handling programs very often
382 * make partitions end on cylinder boundaries.
383 * There is no need to do so, and Linux fdisk doesnt always
384 * do this, and Windows NT on Alpha doesnt do this either,
385 * but still, this helps to guess #heads.
387 for (i
= 0; i
< 4; i
++) {
388 struct partition
*q
= &p
[i
];
390 if ((q
->sector
& 63) == 1 &&
391 (q
->end_sector
& 63) == 63)
392 heads
= q
->end_head
+ 1;
396 if (SYS_IND(p
) == EZD_PARTITION
) {
398 * Accesses to sector 0 must go to sector 1 instead.
400 if (ide_xlate_1024(dev
, -1, heads
, " [EZD]")) {
404 } else if (SYS_IND(p
) == DM6_PARTITION
) {
407 * Everything on the disk is offset by 63 sectors,
408 * including a "new" MBR with its own partition table.
410 if (ide_xlate_1024(dev
, 1, heads
, " [DM6:DDO]")) {
412 goto read_mbr
; /* start over with new MBR */
414 } else if (sig
<= 0x1ae &&
415 data
[sig
] == 0xAA && data
[sig
+1] == 0x55 &&
417 /* DM6 signature in MBR, courtesy of OnTrack */
418 (void) ide_xlate_1024 (dev
, 0, heads
, " [DM6:MBR]");
419 } else if (SYS_IND(p
) == DM6_AUX1PARTITION
||
420 SYS_IND(p
) == DM6_AUX3PARTITION
) {
422 * DM6 on other than the first (boot) drive
424 (void) ide_xlate_1024(dev
, 0, heads
, " [DM6:AUX]");
426 (void) ide_xlate_1024(dev
, 2, heads
, " [PTBL]");
429 #endif /* CONFIG_BLK_DEV_IDE */
431 /* Look for partitions in two passes:
432 First find the primary partitions, and the DOS-type extended partitions.
433 On the second pass look inside *BSD and Unixware and Solaris partitions. */
435 current_minor
+= 4; /* first "extra" minor (for extended partitions) */
436 for (i
=1 ; i
<=4 ; minor
++,i
++,p
++) {
439 add_gd_partition(hd
, minor
, first_sector
+START_SECT(p
)*sector_size
,
440 NR_SECTS(p
)*sector_size
);
441 #if CONFIG_BLK_DEV_MD && CONFIG_AUTODETECT_RAID
442 if (SYS_IND(p
) == LINUX_RAID_PARTITION
) {
443 md_autodetect_dev(MKDEV(hd
->major
,minor
));
446 if (is_extended_partition(p
)) {
449 * If we are rereading the partition table, we need
450 * to set the size of the partition so that we will
451 * be able to bread the block containing the extended
454 hd
->sizes
[minor
] = hd
->part
[minor
].nr_sects
455 >> (BLOCK_SIZE_BITS
- 9);
456 extended_partition(hd
, MKDEV(hd
->major
, minor
));
458 /* prevent someone doing mkfs or mkswap on an
459 extended partition, but leave room for LILO */
460 if (hd
->part
[minor
].nr_sects
> 2)
461 hd
->part
[minor
].nr_sects
= 2;
466 * Check for old-style Disk Manager partition table
468 if (*(unsigned short *) (data
+0xfc) == cpu_to_le16(MSDOS_LABEL_MAGIC
)) {
469 p
= (struct partition
*) (0x1be + data
);
470 for (i
= 4 ; i
< 16 ; i
++, current_minor
++) {
472 if ((current_minor
& mask
) == 0)
474 if (!(START_SECT(p
) && NR_SECTS(p
)))
476 add_gd_partition(hd
, current_minor
, START_SECT(p
), NR_SECTS(p
));
481 /* second pass - output for each on a separate line */
483 p
= (struct partition
*) (0x1be + data
);
484 for (i
=1 ; i
<=4 ; minor
++,i
++,p
++) {
487 #ifdef CONFIG_BSD_DISKLABEL
488 if (SYS_IND(p
) == BSD_PARTITION
||
489 SYS_IND(p
) == NETBSD_PARTITION
||
490 SYS_IND(p
) == OPENBSD_PARTITION
)
491 bsd_disklabel_partition(hd
, minor
, SYS_IND(p
));
493 #ifdef CONFIG_UNIXWARE_DISKLABEL
494 if (SYS_IND(p
) == UNIXWARE_PARTITION
)
495 unixware_partition(hd
, minor
);
497 #ifdef CONFIG_SOLARIS_X86_PARTITION
498 if(SYS_IND(p
) == SOLARIS_X86_PARTITION
)
499 solaris_x86_partition(hd
, minor
);