diff --git a/commands/gptsync.c b/commands/gptsync.c new file mode 100644 index 0000000..ed72ec3 --- /dev/null +++ b/commands/gptsync.c @@ -0,0 +1,252 @@ +/* gptsync.c - fill the mbr based on gpt entries */ +/* XXX: I don't know what to do if sector size isn't 512 bytes */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Convert a LBA address to a CHS address in the INT 13 format. */ +/* Taken from grub1. */ +/* XXX: use hardcoded geometry of C = 1024, H = 255, S = 63. + Is it a problem? +*/ +static void +lba_to_chs (int lba, grub_uint8_t *cl, grub_uint8_t *ch, + grub_uint8_t *dh) +{ + int cylinder, head, sector; + int sectors = 63, heads = 255, cylinders = 1024; + + sector = lba % sectors + 1; + head = (lba / sectors) % heads; + cylinder = lba / (sectors * heads); + + if (cylinder >= cylinders) + { + *cl = *ch = *dh = 0xff; + return; + } + + *cl = sector | ((cylinder & 0x300) >> 2); + *ch = cylinder & 0xFF; + *dh = head; +} + +static grub_err_t +grub_cmd_gptsync (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_device_t dev; + struct grub_pc_partition_mbr mbr; + struct grub_partition *partition; + grub_disk_addr_t first_sector; + int numactive = 0; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + if (argc > 4) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "only 3 partitions can be " + "in hybrid MBR"); + + if (args[0][0] == '(' && args[0][grub_strlen (args[0]) - 1] == ')') + { + args[0][grub_strlen (args[0]) - 1] = 0; + dev = grub_device_open (args[0] + 1); + args[0][grub_strlen (args[0]) - 1] = ')'; + } + else + dev = grub_device_open (args[0]); + + if (! dev) + return grub_errno; + + if (! dev->disk) + { + grub_device_close (dev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "not a disk"); + } + + /* Read the protective MBR. */ + if (grub_disk_read (dev->disk, 0, 0, sizeof (mbr), (char *) &mbr)) + { + grub_device_close (dev); + return grub_errno; + } + + /* Check if it is valid. */ + if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE)) + { + grub_device_close (dev); + return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature"); + } + + /* Make sure the MBR is a protective MBR and not a normal MBR. */ + if (mbr.entries[0].type != GRUB_PC_PARTITION_TYPE_GPT_DISK) + { + grub_device_close (dev); + return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found"); + } + + int i; + first_sector = dev->disk->total_sectors; + for (i = 1; i < argc; i++) + { + char *separator, csep = 0; + grub_uint8_t type; + separator = grub_strchr (args[i], '+'); + if (! separator) + separator = grub_strchr (args[i], '-'); + if (separator) + { + csep = *separator; + *separator = 0; + } + partition = grub_partition_probe (dev->disk, args[i]); + if (separator) + *separator = csep; + if (! partition) + { + grub_device_close (dev); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no such partition"); + } + + if (partition->start + partition->len > 0xffffffff) + { + grub_device_close (dev); + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "only partitions resding in the first 2TB " + "can be presen in hybrid MBR"); + } + + + if (first_sector > partition->start) + first_sector = partition->start; + + if (separator && *(separator + 1)) + type = grub_strtoul (separator + 1, 0, 0); + else + { + grub_fs_t fs = 0; + dev->disk->partition = partition; + fs = grub_fs_probe (dev); + + /* Unknown filesystem isn't fatal. */ + if (grub_errno == GRUB_ERR_UNKNOWN_FS) + { + fs = 0; + grub_errno = GRUB_ERR_NONE; + } + + if (fs && grub_strcmp (fs->name, "ntfs") == 0) + type = GRUB_PC_PARTITION_TYPE_NTFS; + else if (fs && grub_strcmp (fs->name, "fat") == 0) + /* FIXME: detect FAT16. */ + type = GRUB_PC_PARTITION_TYPE_FAT32_LBA; + else + /* FIXME: detect more types. */ + type = GRUB_PC_PARTITION_TYPE_EXT2FS; + + dev->disk->partition = 0; + } + + mbr.entries[i].flag = (csep == '+') ? 0x80 : 0; + if (csep == '+') + { + numactive++; + if (numactive == 2) + { + grub_device_close (dev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "only one partition can be active"); + } + } + mbr.entries[i].type = type; + mbr.entries[i].start = grub_cpu_to_le32 (partition->start); + lba_to_chs (partition->start, + &(mbr.entries[i].start_sector), + &(mbr.entries[i].start_cylinder), + &(mbr.entries[i].start_head)); + lba_to_chs (partition->start + partition->len - 1, + &(mbr.entries[i].end_sector), + &(mbr.entries[i].end_cylinder), + &(mbr.entries[i].end_head)); + mbr.entries[i].length = grub_cpu_to_le32 (partition->len); + grub_free (partition); + } + for (; i < 4; i++) + grub_memset (&(mbr.entries[i]), 0, sizeof (mbr.entries[i])); + + /* The protective partition. */ + if (first_sector > 0xffffffff) + first_sector = 0xffffffff; + else + first_sector--; + mbr.entries[0].flag = 0; + mbr.entries[0].type = GRUB_PC_PARTITION_TYPE_GPT_DISK; + mbr.entries[0].start = grub_cpu_to_le32 (1); + lba_to_chs (1, + &(mbr.entries[0].start_sector), + &(mbr.entries[0].start_cylinder), + &(mbr.entries[0].start_head)); + lba_to_chs (first_sector, + &(mbr.entries[0].end_sector), + &(mbr.entries[0].end_cylinder), + &(mbr.entries[0].end_head)); + mbr.entries[0].length = grub_cpu_to_le32 (first_sector); + + mbr.signature = grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE); + + if (grub_disk_write (dev->disk, 0, 0, sizeof (mbr), (char *) &mbr)) + { + grub_device_close (dev); + return grub_errno; + } + + grub_printf ("New MBR is written to '%s'\n", args[0]); + + return GRUB_ERR_NONE; +} + + +static grub_command_t cmd; + +GRUB_MOD_INIT(gptsync) +{ + (void) mod; /* To stop warning. */ + cmd = grub_register_command ("gptsync", grub_cmd_gptsync, + "gptsync DEVICE [PARTITION[+/-[TYPE]]] ...", + "Fill hybrid MBR of GPT drive DEVICE. " + "specified partitions will be a part " + "of hybrid mbr. Up to 3 partitions are " + "allowed. TYPE is an MBR type. " + "+ means that partition is active. " + "Only one partition can be active"); +} + +GRUB_MOD_FINI(gptsync) +{ + grub_unregister_command (cmd); +} diff --git a/conf/common.rmk b/conf/common.rmk index 0e63da6..c77dce9 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -343,7 +343,12 @@ pkglib_MODULES += minicmd.mod extcmd.mod hello.mod handler.mod \ loopback.mod fs_uuid.mod configfile.mod echo.mod \ terminfo.mod test.mod blocklist.mod hexdump.mod \ read.mod sleep.mod loadenv.mod crc.mod parttool.mod \ - pcpart.mod memrw.mod boot.mod + pcpart.mod memrw.mod boot.mod gptsync.mod + +# For gptsync.mod. +gptsync_mod_SOURCES = commands/gptsync.c +gptsync_mod_CFLAGS = $(COMMON_CFLAGS) +gptsync_mod_LDFLAGS = $(COMMON_LDFLAGS) # For boot.mod. boot_mod_SOURCES = commands/boot.c diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk index f7cb58d..f899f2c 100644 --- a/conf/i386-coreboot.rmk +++ b/conf/i386-coreboot.rmk @@ -59,6 +59,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ commands/configfile.c commands/echo.c commands/help.c \ commands/handler.c commands/ls.c commands/test.c \ commands/search.c commands/blocklist.c commands/hexdump.c \ + commands/gptsync.c \ lib/hexdump.c commands/i386/cpuid.c \ disk/host.c disk/loopback.c \ \ diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index cb41cce..fb50c66 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -59,7 +59,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ commands/handler.c commands/ls.c commands/test.c \ commands/search.c commands/blocklist.c commands/hexdump.c \ lib/hexdump.c commands/halt.c commands/reboot.c \ - commands/i386/cpuid.c \ + commands/gptsync.c commands/i386/cpuid.c \ disk/host.c disk/loopback.c \ \ fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 265b250..ab06d59 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -123,7 +123,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ commands/handler.c commands/ls.c commands/test.c \ commands/search.c commands/blocklist.c commands/hexdump.c \ lib/hexdump.c commands/i386/pc/halt.c commands/reboot.c \ - commands/i386/cpuid.c \ + commands/gptsync.c commands/i386/cpuid.c \ disk/host.c disk/loopback.c disk/scsi.c \ fs/fshelp.c \ \ diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk index d8d35d5..9d0c137 100644 --- a/conf/powerpc-ieee1275.rmk +++ b/conf/powerpc-ieee1275.rmk @@ -43,7 +43,8 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ commands/configfile.c commands/help.c \ commands/search.c commands/handler.c commands/test.c \ commands/ls.c commands/blocklist.c commands/hexdump.c \ - lib/hexdump.c commands/halt.c commands/reboot.c \ + lib/hexdump.c commands/halt.c commands/reboot.c \ + commands/gptsync.c \ disk/loopback.c \ \ fs/affs.c fs/cpio.c fs/fat.c fs/ext2.c fs/hfs.c \ diff --git a/include/grub/pc_partition.h b/include/grub/pc_partition.h index 6a815c3..d4abc51 100644 --- a/include/grub/pc_partition.h +++ b/include/grub/pc_partition.h @@ -35,6 +35,7 @@ #define GRUB_PC_PARTITION_TYPE_FAT16_LT32M 4 #define GRUB_PC_PARTITION_TYPE_EXTENDED 5 #define GRUB_PC_PARTITION_TYPE_FAT16_GT32M 6 +#define GRUB_PC_PARTITION_TYPE_NTFS 7 #define GRUB_PC_PARTITION_TYPE_FAT32 0xb #define GRUB_PC_PARTITION_TYPE_FAT32_LBA 0xc #define GRUB_PC_PARTITION_TYPE_FAT16_LBA 0xe