1 /* gptsync.c - fill the mbr based on gpt entries */
2 /* XXX: I don't know what to do if sector size isn't 512 bytes */
4 * GRUB -- GRand Unified Bootloader
5 * Copyright (C) 2009 Free Software Foundation, Inc.
7 * GRUB is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * GRUB is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
21 #include <grub/command.h>
23 #include <grub/device.h>
24 #include <grub/disk.h>
25 #include <grub/msdos_partition.h>
26 #include <grub/partition.h>
27 #include <grub/misc.h>
30 #include <grub/i18n.h>
32 GRUB_MOD_LICENSE ("GPLv3+");
34 /* Convert a LBA address to a CHS address in the INT 13 format. */
35 /* Taken from grub1. */
36 /* XXX: use hardcoded geometry of C = 1024, H = 255, S = 63.
40 lba_to_chs (grub_uint32_t lba
, grub_uint8_t
*cl
, grub_uint8_t
*ch
,
43 grub_uint32_t cylinder
, head
, sector
;
44 grub_uint32_t sectors
= 63, heads
= 255, cylinders
= 1024;
46 sector
= lba
% sectors
+ 1;
47 head
= (lba
/ sectors
) % heads
;
48 cylinder
= lba
/ (sectors
* heads
);
50 if (cylinder
>= cylinders
)
52 *cl
= *ch
= *dh
= 0xff;
56 *cl
= sector
| ((cylinder
& 0x300) >> 2);
57 *ch
= cylinder
& 0xFF;
62 grub_cmd_gptsync (grub_command_t cmd
__attribute__ ((unused
)),
63 int argc
, char **args
)
66 struct grub_msdos_partition_mbr mbr
;
67 struct grub_partition
*partition
;
68 grub_disk_addr_t first_sector
;
73 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "device name required");
75 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "only 3 partitions can be "
78 if (args
[0][0] == '(' && args
[0][grub_strlen (args
[0]) - 1] == ')')
80 args
[0][grub_strlen (args
[0]) - 1] = 0;
81 dev
= grub_device_open (args
[0] + 1);
82 args
[0][grub_strlen (args
[0])] = ')';
85 dev
= grub_device_open (args
[0]);
92 grub_device_close (dev
);
93 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "not a disk");
96 /* Read the protective MBR. */
97 if (grub_disk_read (dev
->disk
, 0, 0, sizeof (mbr
), &mbr
))
99 grub_device_close (dev
);
103 /* Check if it is valid. */
104 if (mbr
.signature
!= grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE
))
106 grub_device_close (dev
);
107 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "no signature");
110 /* Make sure the MBR is a protective MBR and not a normal MBR. */
111 for (i
= 0; i
< 4; i
++)
112 if (mbr
.entries
[i
].type
== GRUB_PC_PARTITION_TYPE_GPT_DISK
)
116 grub_device_close (dev
);
117 return grub_error (GRUB_ERR_BAD_PART_TABLE
, "no GPT partition map found");
120 first_sector
= dev
->disk
->total_sectors
;
121 for (i
= 1; i
< argc
; i
++)
123 char *separator
, csep
= 0;
125 separator
= grub_strchr (args
[i
], '+');
127 separator
= grub_strchr (args
[i
], '-');
133 partition
= grub_partition_probe (dev
->disk
, args
[i
]);
138 grub_device_close (dev
);
139 return grub_error (GRUB_ERR_UNKNOWN_DEVICE
,
140 N_("no such partition"));
143 if (partition
->start
+ partition
->len
> 0xffffffff)
145 grub_device_close (dev
);
146 return grub_error (GRUB_ERR_OUT_OF_RANGE
,
147 "only partitions residing in the first 2TB "
148 "can be present in hybrid MBR");
152 if (first_sector
> partition
->start
)
153 first_sector
= partition
->start
;
155 if (separator
&& *(separator
+ 1))
156 type
= grub_strtoul (separator
+ 1, 0, 0);
160 dev
->disk
->partition
= partition
;
161 fs
= grub_fs_probe (dev
);
163 /* Unknown filesystem isn't fatal. */
164 if (grub_errno
== GRUB_ERR_UNKNOWN_FS
)
167 grub_errno
= GRUB_ERR_NONE
;
170 if (fs
&& grub_strcmp (fs
->name
, "ntfs") == 0)
171 type
= GRUB_PC_PARTITION_TYPE_NTFS
;
172 else if (fs
&& grub_strcmp (fs
->name
, "fat") == 0)
173 /* FIXME: detect FAT16. */
174 type
= GRUB_PC_PARTITION_TYPE_FAT32_LBA
;
175 else if (fs
&& (grub_strcmp (fs
->name
, "hfsplus") == 0
176 || grub_strcmp (fs
->name
, "hfs") == 0))
177 type
= GRUB_PC_PARTITION_TYPE_HFS
;
179 /* FIXME: detect more types. */
180 type
= GRUB_PC_PARTITION_TYPE_EXT2FS
;
182 dev
->disk
->partition
= 0;
185 mbr
.entries
[i
].flag
= (csep
== '+') ? 0x80 : 0;
191 grub_device_close (dev
);
192 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
193 "only one partition can be active");
196 mbr
.entries
[i
].type
= type
;
197 mbr
.entries
[i
].start
= grub_cpu_to_le32 (partition
->start
);
198 lba_to_chs (partition
->start
,
199 &(mbr
.entries
[i
].start_sector
),
200 &(mbr
.entries
[i
].start_cylinder
),
201 &(mbr
.entries
[i
].start_head
));
202 lba_to_chs (partition
->start
+ partition
->len
- 1,
203 &(mbr
.entries
[i
].end_sector
),
204 &(mbr
.entries
[i
].end_cylinder
),
205 &(mbr
.entries
[i
].end_head
));
206 mbr
.entries
[i
].length
= grub_cpu_to_le32 (partition
->len
);
207 grub_free (partition
);
210 grub_memset (&(mbr
.entries
[i
]), 0, sizeof (mbr
.entries
[i
]));
212 /* The protective partition. */
213 if (first_sector
> 0xffffffff)
214 first_sector
= 0xffffffff;
217 mbr
.entries
[0].flag
= 0;
218 mbr
.entries
[0].type
= GRUB_PC_PARTITION_TYPE_GPT_DISK
;
219 mbr
.entries
[0].start
= grub_cpu_to_le32_compile_time (1);
221 &(mbr
.entries
[0].start_sector
),
222 &(mbr
.entries
[0].start_cylinder
),
223 &(mbr
.entries
[0].start_head
));
224 lba_to_chs (first_sector
,
225 &(mbr
.entries
[0].end_sector
),
226 &(mbr
.entries
[0].end_cylinder
),
227 &(mbr
.entries
[0].end_head
));
228 mbr
.entries
[0].length
= grub_cpu_to_le32 (first_sector
);
230 mbr
.signature
= grub_cpu_to_le16_compile_time (GRUB_PC_PARTITION_SIGNATURE
);
232 if (grub_disk_write (dev
->disk
, 0, 0, sizeof (mbr
), &mbr
))
234 grub_device_close (dev
);
238 grub_device_close (dev
);
240 grub_printf_ (N_("New MBR is written to `%s'\n"), args
[0]);
242 return GRUB_ERR_NONE
;
246 static grub_command_t cmd
;
248 GRUB_MOD_INIT(gptsync
)
250 (void) mod
; /* To stop warning. */
251 cmd
= grub_register_command ("gptsync", grub_cmd_gptsync
,
252 N_("DEVICE [PARTITION[+/-[TYPE]]] ..."),
253 /* TRANSLATORS: MBR type is one-byte partition
255 N_("Fill hybrid MBR of GPT drive DEVICE. "
256 "Specified partitions will be a part "
257 "of hybrid MBR. Up to 3 partitions are "
258 "allowed. TYPE is an MBR type. "
259 "+ means that partition is active. "
260 "Only one partition can be active."));
263 GRUB_MOD_FINI(gptsync
)
265 grub_unregister_command (cmd
);