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
/* Convert a LBA address to a CHS address in the INT 13 format. */
33
/* Taken from grub1. */
34
/* XXX: use hardcoded geometry of C = 1024, H = 255, S = 63.
38
lba_to_chs (int lba, grub_uint8_t *cl, grub_uint8_t *ch,
41
int cylinder, head, sector;
42
int sectors = 63, heads = 255, cylinders = 1024;
44
sector = lba % sectors + 1;
45
head = (lba / sectors) % heads;
46
cylinder = lba / (sectors * heads);
48
if (cylinder >= cylinders)
50
*cl = *ch = *dh = 0xff;
54
*cl = sector | ((cylinder & 0x300) >> 2);
55
*ch = cylinder & 0xFF;
60
grub_cmd_gptsync (grub_command_t cmd __attribute__ ((unused)),
61
int argc, char **args)
64
struct grub_msdos_partition_mbr mbr;
65
struct grub_partition *partition;
66
grub_disk_addr_t first_sector;
70
return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
72
return grub_error (GRUB_ERR_BAD_ARGUMENT, "only 3 partitions can be "
75
if (args[0][0] == '(' && args[0][grub_strlen (args[0]) - 1] == ')')
77
args[0][grub_strlen (args[0]) - 1] = 0;
78
dev = grub_device_open (args[0] + 1);
79
args[0][grub_strlen (args[0])] = ')';
82
dev = grub_device_open (args[0]);
89
grub_device_close (dev);
90
return grub_error (GRUB_ERR_BAD_ARGUMENT, "not a disk");
93
/* Read the protective MBR. */
94
if (grub_disk_read (dev->disk, 0, 0, sizeof (mbr), &mbr))
96
grub_device_close (dev);
100
/* Check if it is valid. */
101
if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE))
103
grub_device_close (dev);
104
return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature");
107
/* Make sure the MBR is a protective MBR and not a normal MBR. */
108
if (mbr.entries[0].type != GRUB_PC_PARTITION_TYPE_GPT_DISK)
110
grub_device_close (dev);
111
return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found");
115
first_sector = dev->disk->total_sectors;
116
for (i = 1; i < argc; i++)
118
char *separator, csep = 0;
120
separator = grub_strchr (args[i], '+');
122
separator = grub_strchr (args[i], '-');
128
partition = grub_partition_probe (dev->disk, args[i]);
133
grub_device_close (dev);
134
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such partition");
137
if (partition->start + partition->len > 0xffffffff)
139
grub_device_close (dev);
140
return grub_error (GRUB_ERR_OUT_OF_RANGE,
141
"only partitions residing in the first 2TB "
142
"can be present in hybrid MBR");
146
if (first_sector > partition->start)
147
first_sector = partition->start;
149
if (separator && *(separator + 1))
150
type = grub_strtoul (separator + 1, 0, 0);
154
dev->disk->partition = partition;
155
fs = grub_fs_probe (dev);
157
/* Unknown filesystem isn't fatal. */
158
if (grub_errno == GRUB_ERR_UNKNOWN_FS)
161
grub_errno = GRUB_ERR_NONE;
164
if (fs && grub_strcmp (fs->name, "ntfs") == 0)
165
type = GRUB_PC_PARTITION_TYPE_NTFS;
166
else if (fs && grub_strcmp (fs->name, "fat") == 0)
167
/* FIXME: detect FAT16. */
168
type = GRUB_PC_PARTITION_TYPE_FAT32_LBA;
169
else if (fs && (grub_strcmp (fs->name, "hfsplus") == 0
170
|| grub_strcmp (fs->name, "hfs") == 0))
171
type = GRUB_PC_PARTITION_TYPE_HFS;
173
/* FIXME: detect more types. */
174
type = GRUB_PC_PARTITION_TYPE_EXT2FS;
176
dev->disk->partition = 0;
179
mbr.entries[i].flag = (csep == '+') ? 0x80 : 0;
185
grub_device_close (dev);
186
return grub_error (GRUB_ERR_BAD_ARGUMENT,
187
"only one partition can be active");
190
mbr.entries[i].type = type;
191
mbr.entries[i].start = grub_cpu_to_le32 (partition->start);
192
lba_to_chs (partition->start,
193
&(mbr.entries[i].start_sector),
194
&(mbr.entries[i].start_cylinder),
195
&(mbr.entries[i].start_head));
196
lba_to_chs (partition->start + partition->len - 1,
197
&(mbr.entries[i].end_sector),
198
&(mbr.entries[i].end_cylinder),
199
&(mbr.entries[i].end_head));
200
mbr.entries[i].length = grub_cpu_to_le32 (partition->len);
201
grub_free (partition);
204
grub_memset (&(mbr.entries[i]), 0, sizeof (mbr.entries[i]));
206
/* The protective partition. */
207
if (first_sector > 0xffffffff)
208
first_sector = 0xffffffff;
211
mbr.entries[0].flag = 0;
212
mbr.entries[0].type = GRUB_PC_PARTITION_TYPE_GPT_DISK;
213
mbr.entries[0].start = grub_cpu_to_le32 (1);
215
&(mbr.entries[0].start_sector),
216
&(mbr.entries[0].start_cylinder),
217
&(mbr.entries[0].start_head));
218
lba_to_chs (first_sector,
219
&(mbr.entries[0].end_sector),
220
&(mbr.entries[0].end_cylinder),
221
&(mbr.entries[0].end_head));
222
mbr.entries[0].length = grub_cpu_to_le32 (first_sector);
224
mbr.signature = grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE);
226
if (grub_disk_write (dev->disk, 0, 0, sizeof (mbr), &mbr))
228
grub_device_close (dev);
232
grub_printf ("New MBR is written to '%s'\n", args[0]);
234
return GRUB_ERR_NONE;
238
static grub_command_t cmd;
240
GRUB_MOD_INIT(gptsync)
242
(void) mod; /* To stop warning. */
243
cmd = grub_register_command ("gptsync", grub_cmd_gptsync,
244
N_("DEVICE [PARTITION[+/-[TYPE]]] ..."),
245
N_("Fill hybrid MBR of GPT drive DEVICE. "
246
"Specified partitions will be a part "
247
"of hybrid MBR. Up to 3 partitions are "
248
"allowed. TYPE is an MBR type. "
249
"+ means that partition is active. "
250
"Only one partition can be active."));
253
GRUB_MOD_FINI(gptsync)
255
grub_unregister_command (cmd);