* [PATCH] Split of raid scan code @ 2008-08-10 11:52 Bean 2008-08-10 12:13 ` Felix Zielcke 2008-08-10 14:29 ` Robert Millan 0 siblings, 2 replies; 24+ messages in thread From: Bean @ 2008-08-10 11:52 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 381 bytes --] Hi, This patch seperate the scan code from raid.c, and move it to mdraid.c. raid.c only contain generic raid handling code now. This make it much easier to extend to other raid layout, like various fakeraids. I also extend the function of grub-fstest so that it can be used to debug raid, for example, you can run it like this: grub-fstest -c 2 /dev/hdb1 /dev/hdd1 ls -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_2.diff --] [-- Type: text/x-diff; name=raid_2.diff, Size: 38884 bytes --] diff --git a/conf/common.rmk b/conf/common.rmk index 3d674a6..dee5d54 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -35,6 +35,7 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/mdraid.c \ grub_fstest_init.c # For the parser. @@ -257,7 +258,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod lvm.mod mdraid.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -269,6 +270,11 @@ lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..b40bb13 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -121,6 +121,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; total_sectors = 9000000; /* TODO: get the correct size. */ + disk->size_invalid = 1; } else if (drive & 0x80) { diff --git a/disk/mdraid.c b/disk/mdraid.c new file mode 100644 index 0000000..0552436 --- /dev/null +++ b/disk/mdraid.c @@ -0,0 +1,245 @@ +/* mdraid.c - module to handle mdraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define GRUB_RAID_RESERVED_BYTES (64 * 1024) +#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) + +#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ + - GRUB_RAID_RESERVED_SECTORS) + +#define GRUB_RAID_SB_BYTES 4096 +#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) +#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define GRUB_RAID_SB_GENERIC_OFFSET 0 + +#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 +#define GRUB_RAID_SB_DISKS_OFFSET 128 +#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 + +#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 +#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 +#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ + + GRUB_RAID_SB_GENERIC_STATE_WORDS) +#define GRUB_RAID_SB_PERSONALITY_WORDS 64 +#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 +#define GRUB_RAID_SB_DISKS 27 +#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ + - GRUB_RAID_SB_PERSONALITY_WORDS \ + - GRUB_RAID_SB_DISKS_WORDS \ + - GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ + + GRUB_RAID_SB_PERSONALITY_WORDS \ + + GRUB_RAID_SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ +#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ +#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ +#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + +#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. + * read requests will only be sent here in + * dire need + */ + + +#define GRUB_RAID_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define GRUB_RAID_SB_CLEAN 0 +#define GRUB_RAID_SB_ERRORS 1 + +#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + +struct grub_raid_disk_09 { + grub_uint32_t number; /* 0 Device number in the entire set */ + grub_uint32_t major; /* 1 Device major number */ + grub_uint32_t minor; /* 2 Device minor number */ + grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ + grub_uint32_t state; /* 4 Operational state */ + grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 { + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* 0 MD identifier */ + grub_uint32_t major_version; /* 1 major version to which the set conforms */ + grub_uint32_t minor_version; /* 2 minor version ... */ + grub_uint32_t patch_version; /* 3 patchlevel version ... */ + grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ + grub_uint32_t set_uuid0; /* 5 Raid set identifier */ + grub_uint32_t ctime; /* 6 Creation time */ + grub_uint32_t level; /* 7 Raid personality */ + grub_uint32_t size; /* 8 Apparent size of each individual disk */ + grub_uint32_t nr_disks; /* 9 total disks in the raid set */ + grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ + grub_uint32_t md_minor; /* 11 preferred MD minor device number */ + grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ + grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ + grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ + grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ + grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* 0 Superblock update time */ + grub_uint32_t state; /* 1 State bits (clean, ...) */ + grub_uint32_t active_disks; /* 2 Number of currently active disks */ + grub_uint32_t working_disks; /* 3 Number of working disks */ + grub_uint32_t failed_disks; /* 4 Number of failed disks */ + grub_uint32_t spare_disks; /* 5 Number of spare disks */ + grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ +#ifdef GRUB_HOST_WORDS_BIGENDIAN + grub_uint32_t events_hi; /* 7 high-order of superblock update count */ + grub_uint32_t events_lo; /* 8 low-order of superblock update count */ + grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ + grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ +#else + grub_uint32_t events_lo; /* 7 low-order of superblock update count */ + grub_uint32_t events_hi; /* 8 high-order of superblock update count */ + grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ + grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ +#endif + grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ + grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* 0 the array's physical layout */ + grub_uint32_t chunk_size; /* 1 chunk size in bytes */ + grub_uint32_t root_pv; /* 2 LV root PV */ + grub_uint32_t root_block; /* 3 LV root block */ + grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +}; + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = GRUB_RAID_NEW_SIZE_SECTORS(size); + + if (grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != GRUB_RAID_SB_MAGIC) + return grub_error (GRUB_ERR_BAD_FS, "not raid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + /* FIXME: Support all RAID levels. */ + if (sb.level != 0 && sb.level != 1 && sb.level != 5) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", + sb.level); + + /* FIXME: Support all layouts. */ + if (sb.level == 5 && sb.layout != 2) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID5 layout: %d", + sb.layout); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = sb.layout; + array->total_devs = sb.nr_disks; + array->disk_size = sb.size * 2; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (! array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = +{ + .name = "md", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); + grub_raid_rescan (); +} + +GRUB_MOD_FINI(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 731bf1f..bdfb03d 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -113,7 +113,17 @@ grub_raid_open (const char *name, grub_disk_t disk) { case 0: /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; + if (array->disk_size) + disk->total_sectors = array->total_devs * array->disk_size; + else + { + unsigned i; + + disk->total_sectors = 0; + for (i = 0; i < array->total_devs; i++) + disk->total_sectors += array->device[i]->total_sectors; + } + break; case 1: @@ -348,167 +358,122 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) -{ - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); +static grub_raid_t grub_raid_list; - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; +} - /* FIXME: Check the checksum. */ +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *device_name) +{ + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } - - array->name = grub_malloc (13); + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } + + array->name = grub_malloc (grub_strlen (device_name) + 6); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } - grub_sprintf (array->name, "md%d", array->number); + grub_sprintf (array->name, "%s%d", device_name, array->number); grub_dprintf ("raid", "Found array: %s\n", array->name); @@ -518,49 +483,85 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; + + return 0; +} + +static int +grub_raid_scan_device (const char *name) +{ + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) + disk = grub_disk_open (name); + if (!disk) + return 0; + + if (disk->size_invalid) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_close (disk); + return 0; } - if (! array->device[sb.this_disk.number]) + for (p = grub_raid_list; p; p = p->next) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + return 0; - return 0; + break; + } + + grub_errno = GRUB_ERR_NONE; } - array->nr_devs++; + grub_disk_close (disk); return 0; } +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) + { + struct grub_raid_array *p; + int i; + + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); + } + + array_list = 0; +} + +void +grub_raid_rescan (void) +{ + if (grub_raid_list) + { + free_array (); + grub_device_iterate (&grub_raid_scan_device); + } +} + static struct grub_disk_dev grub_raid_dev = { .name = "raid", @@ -579,12 +580,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/include/grub/disk.h b/include/grub/disk.h index 049cc91..758cdda 100644 --- a/include/grub/disk.h +++ b/include/grub/disk.h @@ -93,6 +93,9 @@ struct grub_disk /* The underlying disk device. */ grub_disk_dev_t dev; + /* If total_sectors is invalid. */ + int size_invalid; + /* The total number of sectors. */ grub_uint64_t total_sectors; diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..ca2edb7 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,43 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ int layout; /* Only for RAID 5. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) - -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) - -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ - -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ - - -#define GRUB_RAID_SB_MAGIC 0xa92b4efc - -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 +struct grub_raid +{ + const char *name; -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; + struct grub_raid *next; }; +typedef struct grub_raid *grub_raid_t; -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +void grub_raid_rescan (void); #endif /* ! GRUB_RAID_H */ diff --git a/kern/disk.c b/kern/disk.c index ed82506..804ba5f 100644 --- a/kern/disk.c +++ b/kern/disk.c @@ -227,6 +227,7 @@ grub_disk_open (const char *name) disk->read_hook = 0; disk->partition = 0; disk->data = 0; + disk->size_invalid = 0; disk->name = grub_strdup (name); if (! disk->name) goto fail; diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..29234ac 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,6 +29,7 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> #include <grub_fstest_init.h> @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -273,32 +273,61 @@ cmd_hex (char *pathname) return 0; } - read_file (pathname, hex_hook); + if (pathname) + read_file (pathname, hex_hook); + else + { + char buf[BUF_SIZE]; + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device"); + + if (! leng) + leng = GRUB_DISK_SECTOR_SIZE; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d\n", + skip, len); + + hexdump (skip, buf, len); + + skip += len; + leng -= len; + } + + grub_device_close (dev); + } } static void -fstest (char *image_path, int cmd, int n, char **args) +fstest (char **images, int num_disks, int cmd, int n, char **args) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; - - - grub_sprintf (host_file, "(host)/%s", image_path); + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails.\n"); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) cmd_cmp (args[0], args[1]); break; case CMD_HEX: - cmd_hex (args[0]); + cmd_hex ((n == 0) ? 0 : args[0]); break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ ls PATH list files in PATH\n\ cp SRC DEST copy file to local system\n\ cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + hex [FILE] Hex dump FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,35 +481,29 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) - { - fprintf (stderr, "No path is specified.\n"); - usage (1); - } - - image_path = argv[optind]; - - if (*image_path != '/') + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "Must use absolute path.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; @@ -478,6 +521,7 @@ main (int argc, char *argv[]) else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +547,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-10 11:52 [PATCH] Split of raid scan code Bean @ 2008-08-10 12:13 ` Felix Zielcke 2008-08-10 14:29 ` Robert Millan 1 sibling, 0 replies; 24+ messages in thread From: Felix Zielcke @ 2008-08-10 12:13 UTC (permalink / raw) To: The development of GRUB 2; +Cc: 494501-forwarded Am Sonntag, den 10.08.2008, 19:52 +0800 schrieb Bean: > Hi, > > This patch seperate the scan code from raid.c, and move it to > mdraid.c. raid.c only contain generic raid handling code now. This > make it much easier to extend to other raid layout, like various > fakeraids. > Really great that you work now on the RAID Code. Today morning (for me) there just came another bugreport on Debian BTS in about RAID [0] This should probable go into lenny and so I probable have to make a change for this anyway. It's just the same as with these invalid BSD disklabels/partitions. The return grub_error should be better changed to an grub_dprintf() + return 0; for unknown RAID Levels. Unknown RAID levels should be better just ignored, i.e. just not added to the RAID array list instead of whole GRUB dropping to rescue mode. [0] http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=494501 ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-10 11:52 [PATCH] Split of raid scan code Bean 2008-08-10 12:13 ` Felix Zielcke @ 2008-08-10 14:29 ` Robert Millan 2008-08-10 14:45 ` Bean 1 sibling, 1 reply; 24+ messages in thread From: Robert Millan @ 2008-08-10 14:29 UTC (permalink / raw) To: The development of GRUB 2 On Sun, Aug 10, 2008 at 07:52:22PM +0800, Bean wrote: > kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ > kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ > + disk/mdraid.c \ Perhaps it'd be a good idea to take the opportunity to add a "linux" prefix there to indicate it's a Linux-specific feature, and call it disk/linux_mdraid.c or so. Btw, how does this affect grub-install / update-grub ? Both currently detect RAID by using grub-probe -t abstraction, and load raid.mod when necessary. Maybe with your patch they will need some adjustment. -- Robert Millan The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and how) you may access your data; but nobody's threatening your freedom: we still allow you to remove your data and not access it at all." ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-10 14:29 ` Robert Millan @ 2008-08-10 14:45 ` Bean 2008-08-10 15:49 ` Bean 0 siblings, 1 reply; 24+ messages in thread From: Bean @ 2008-08-10 14:45 UTC (permalink / raw) To: The development of GRUB 2 On Sun, Aug 10, 2008 at 10:29 PM, Robert Millan <rmh@aybabtu.com> wrote: > On Sun, Aug 10, 2008 at 07:52:22PM +0800, Bean wrote: >> kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ >> kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ >> + disk/mdraid.c \ > > Perhaps it'd be a good idea to take the opportunity to add a "linux" prefix > there to indicate it's a Linux-specific feature, and call it > disk/linux_mdraid.c or so. > > Btw, how does this affect grub-install / update-grub ? Both currently detect > RAID by using grub-probe -t abstraction, and load raid.mod when necessary. > Maybe with your patch they will need some adjustment. Hi, Oh, I haven't fixed grub-probe yet. I'm writing a fakeraid driver now, and see if there will be any problem using more than one scanner. -- Bean ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-10 14:45 ` Bean @ 2008-08-10 15:49 ` Bean 2008-08-11 18:45 ` Bean 0 siblings, 1 reply; 24+ messages in thread From: Bean @ 2008-08-10 15:49 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 125 bytes --] Hi, Ok, this is another patch. It changes a few things, and add support for Nvidia Fakeraid through dm_nv module. -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_3.diff --] [-- Type: text/x-diff; name=raid_3.diff, Size: 43685 bytes --] diff --git a/conf/common.rmk b/conf/common.rmk index 3d674a6..58c7b98 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -35,6 +35,7 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c # For the parser. @@ -257,7 +258,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod lvm.mod mdraid.mod dm_nv.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -269,6 +270,16 @@ lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c new file mode 100644 index 0000000..3bded76 --- /dev/null +++ b/disk/dmraid_nvidia.c @@ -0,0 +1,159 @@ +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +#define NV_SIGNATURES 4 + +#define NV_IDLE 0 +#define NV_SCDB_INIT_RAID 2 +#define NV_SCDB_REBUILD_RAID 3 +#define NV_SCDB_UPGRADE_RAID 4 +#define NV_SCDB_SYNC_RAID 5 + +#define NV_LEVEL_UNKNOWN 0x00 +#define NV_LEVEL_JBOD 0xFF +#define NV_LEVEL_0 0x80 +#define NV_LEVEL_1 0x81 +#define NV_LEVEL_3 0x83 +#define NV_LEVEL_5 0x85 +#define NV_LEVEL_10 0x8a +#define NV_LEVEL_1_0 0x8180 + +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ + +struct grub_raid_nv_array +{ + grub_uint32_t version; + grub_uint32_t signature[NV_SIGNATURES]; + grub_uint8_t raid_job_code; + grub_uint8_t stripe_width; + grub_uint8_t total_volumes; + grub_uint8_t original_width; + grub_uint32_t raid_level; + grub_uint32_t stripe_block_size; + grub_uint32_t stripe_block_size_bytes; + grub_uint32_t stripe_block_size_log2; + grub_uint32_t stripe_mask; + grub_uint32_t stripe_size; + grub_uint32_t stripe_size_bytes; + grub_uint32_t raid_job_mask; + grub_uint32_t original_capacity; + grub_uint32_t flags; +}; + +#define NV_ID_LENGTH 8 +#define NV_ID_STRING "NVIDIA" +#define NV_VERSION 100 + +#define NV_PRODUCTIDS 16 +#define NV_PRODUCTREVISIONS 4 + +struct grub_raid_nv_super +{ + grub_uint8_t vendor[NV_ID_LENGTH]; /* 0x00 - 0x07 ID string. */ + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ + grub_uint8_t reserved; /* 0x13. */ + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ + grub_uint8_t product_id[NV_PRODUCTIDS]; /* 0x1C - 0x2B Array product ID. */ + grub_uint8_t product_rev[NV_PRODUCTREVISIONS]; /* 0x2C - 0x2F Array product revision */ + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ + struct grub_raid_nv_array array; /* Array information */ +} __attribute__ ((packed)); + +static grub_err_t +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + struct grub_raid_nv_super sb; + grub_uint32_t *uuid; + + sector = grub_disk_get_size (disk) - 2; + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) + return grub_errno; + + if (! grub_memcmp (sb.vendor, NV_ID_STRING, 6)) + return grub_error (GRUB_ERR_BAD_FS, "not raid"); + + if (sb.version != NV_VERSION) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unknown version: %d.%d", sb.version); + + switch (sb.array.raid_level) + { + case NV_LEVEL_0: + array->level = 0; + break; + + case NV_LEVEL_1: + array->level = 1; + break; + + case NV_LEVEL_5: + array->level = 1; + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.array.raid_level); + } + + array->number = 0; + array->total_devs = sb.array.total_volumes; + array->disk_size = sb.capacity / sb.array.total_volumes; + array->chunk_size = sb.array.stripe_size; + array->index = sb.unit_number; + array->uuid_len = sizeof (sb.array.signature); + array->uuid = grub_malloc (sizeof (sb.array.signature)); + if (! array->uuid) + return grub_errno; + + grub_memcpy (array->uuid, (char *) &sb.array.signature, + sizeof (sb.array.signature)); + + return 0; +} + +static struct grub_raid grub_dmraid_nv_dev = +{ + .name = "dmraid_nv", + .detect = grub_dmraid_nv_detect, + .next = 0 +}; + +GRUB_MOD_INIT(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} + +GRUB_MOD_FINI(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..8b7f5ed 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; - total_sectors = 9000000; /* TODO: get the correct size. */ + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c new file mode 100644 index 0000000..d2b7b18 --- /dev/null +++ b/disk/mdraid_linux.c @@ -0,0 +1,244 @@ +/* mdraid_linux.c - module to handle linux softraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define GRUB_RAID_RESERVED_BYTES (64 * 1024) +#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) + +#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ + - GRUB_RAID_RESERVED_SECTORS) + +#define GRUB_RAID_SB_BYTES 4096 +#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) +#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define GRUB_RAID_SB_GENERIC_OFFSET 0 + +#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 +#define GRUB_RAID_SB_DISKS_OFFSET 128 +#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 + +#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 +#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 +#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ + + GRUB_RAID_SB_GENERIC_STATE_WORDS) +#define GRUB_RAID_SB_PERSONALITY_WORDS 64 +#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 +#define GRUB_RAID_SB_DISKS 27 +#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ + - GRUB_RAID_SB_PERSONALITY_WORDS \ + - GRUB_RAID_SB_DISKS_WORDS \ + - GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ + + GRUB_RAID_SB_PERSONALITY_WORDS \ + + GRUB_RAID_SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ +#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ +#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ +#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + +#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. + * read requests will only be sent here in + * dire need + */ + + +#define GRUB_RAID_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define GRUB_RAID_SB_CLEAN 0 +#define GRUB_RAID_SB_ERRORS 1 + +#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + +struct grub_raid_disk_09 { + grub_uint32_t number; /* 0 Device number in the entire set */ + grub_uint32_t major; /* 1 Device major number */ + grub_uint32_t minor; /* 2 Device minor number */ + grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ + grub_uint32_t state; /* 4 Operational state */ + grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 { + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* 0 MD identifier */ + grub_uint32_t major_version; /* 1 major version to which the set conforms */ + grub_uint32_t minor_version; /* 2 minor version ... */ + grub_uint32_t patch_version; /* 3 patchlevel version ... */ + grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ + grub_uint32_t set_uuid0; /* 5 Raid set identifier */ + grub_uint32_t ctime; /* 6 Creation time */ + grub_uint32_t level; /* 7 Raid personality */ + grub_uint32_t size; /* 8 Apparent size of each individual disk */ + grub_uint32_t nr_disks; /* 9 total disks in the raid set */ + grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ + grub_uint32_t md_minor; /* 11 preferred MD minor device number */ + grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ + grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ + grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ + grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ + grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* 0 Superblock update time */ + grub_uint32_t state; /* 1 State bits (clean, ...) */ + grub_uint32_t active_disks; /* 2 Number of currently active disks */ + grub_uint32_t working_disks; /* 3 Number of working disks */ + grub_uint32_t failed_disks; /* 4 Number of failed disks */ + grub_uint32_t spare_disks; /* 5 Number of spare disks */ + grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ +#ifdef GRUB_HOST_WORDS_BIGENDIAN + grub_uint32_t events_hi; /* 7 high-order of superblock update count */ + grub_uint32_t events_lo; /* 8 low-order of superblock update count */ + grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ + grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ +#else + grub_uint32_t events_lo; /* 7 low-order of superblock update count */ + grub_uint32_t events_hi; /* 8 high-order of superblock update count */ + grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ + grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ +#endif + grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ + grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* 0 the array's physical layout */ + grub_uint32_t chunk_size; /* 1 chunk size in bytes */ + grub_uint32_t root_pv; /* 2 LV root PV */ + grub_uint32_t root_block; /* 3 LV root block */ + grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +}; + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = GRUB_RAID_NEW_SIZE_SECTORS(size); + + if (grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != GRUB_RAID_SB_MAGIC) + return grub_error (GRUB_ERR_BAD_FS, "not raid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + /* FIXME: Support all RAID levels. */ + if (sb.level != 0 && sb.level != 1 && sb.level != 5) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", + sb.level); + + /* FIXME: Support all layouts. */ + if (sb.level == 5 && sb.layout != 2) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID5 layout: %d", + sb.layout); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = sb.layout; + array->total_devs = sb.nr_disks; + array->disk_size = (sb.size) ? sb.size * 2 : sector - 1; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (! array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = +{ + .name = "mdraid", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} + +GRUB_MOD_FINI(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 731bf1f..76ed5ff 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -112,9 +112,15 @@ grub_raid_open (const char *name, grub_disk_t disk) switch (array->level) { case 0: - /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; - break; + { + unsigned i; + + disk->total_sectors = 0; + for (i = 0; i < array->total_devs; i++) + disk->total_sectors += array->device[i]->total_sectors; + + break; + } case 1: disk->total_sectors = array->disk_size; @@ -348,169 +354,103 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *scanner_name) { - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); - - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } - - /* FIXME: Check the checksum. */ - - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } - - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } array->name = grub_malloc (13); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } grub_sprintf (array->name, "md%d", array->number); - grub_dprintf ("raid", "Found array: %s\n", array->name); + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, + scanner_name); /* Add our new array to the list. */ array->next = array_list; @@ -518,47 +458,112 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; + + return 0; +} + - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) +static grub_raid_t grub_raid_list; + +static void +grub_raid_scan_device (int head_only) +{ + auto int hook (const char *name); + int hook (const char *name) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); + + disk = grub_disk_open (name); + if (!disk) + return 0; + + if (disk->total_sectors == ULONG_MAX) + { + grub_disk_close (disk); + return 0; + } + + for (p = grub_raid_list; p; p = p->next) + { + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + return 0; + + break; + } + + grub_errno = GRUB_ERR_NONE; + if (head_only) + break; + } + + grub_disk_close (disk); + + return 0; } - if (! array->device[sb.this_disk.number]) + grub_device_iterate (&hook); +} + +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + struct grub_raid_array *p; + int i; - return 0; + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); } - array->nr_devs++; + array_list = 0; +} - return 0; +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; + grub_raid_scan_device (1); +} + +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; + + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} + +void +grub_raid_rescan (void) +{ + free_array (); + grub_raid_scan_device (0); } static struct grub_disk_dev grub_raid_dev = @@ -579,12 +584,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..ca2edb7 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,43 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ int layout; /* Only for RAID 5. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) - -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) - -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ - -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ - - -#define GRUB_RAID_SB_MAGIC 0xa92b4efc - -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 +struct grub_raid +{ + const char *name; -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; + struct grub_raid *next; }; +typedef struct grub_raid *grub_raid_t; -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +void grub_raid_rescan (void); #endif /* ! GRUB_RAID_H */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..29234ac 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,6 +29,7 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> #include <grub_fstest_init.h> @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -273,32 +273,61 @@ cmd_hex (char *pathname) return 0; } - read_file (pathname, hex_hook); + if (pathname) + read_file (pathname, hex_hook); + else + { + char buf[BUF_SIZE]; + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device"); + + if (! leng) + leng = GRUB_DISK_SECTOR_SIZE; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d\n", + skip, len); + + hexdump (skip, buf, len); + + skip += len; + leng -= len; + } + + grub_device_close (dev); + } } static void -fstest (char *image_path, int cmd, int n, char **args) +fstest (char **images, int num_disks, int cmd, int n, char **args) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; - - - grub_sprintf (host_file, "(host)/%s", image_path); + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails.\n"); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) cmd_cmp (args[0], args[1]); break; case CMD_HEX: - cmd_hex (args[0]); + cmd_hex ((n == 0) ? 0 : args[0]); break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ ls PATH list files in PATH\n\ cp SRC DEST copy file to local system\n\ cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + hex [FILE] Hex dump FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,35 +481,29 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) - { - fprintf (stderr, "No path is specified.\n"); - usage (1); - } - - image_path = argv[optind]; - - if (*image_path != '/') + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "Must use absolute path.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; @@ -478,6 +521,7 @@ main (int argc, char *argv[]) else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +547,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-10 15:49 ` Bean @ 2008-08-11 18:45 ` Bean 2008-08-11 20:13 ` Bean 0 siblings, 1 reply; 24+ messages in thread From: Bean @ 2008-08-11 18:45 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 319 bytes --] On Sun, Aug 10, 2008 at 11:49 PM, Bean <bean123ch@gmail.com> wrote: > Hi, > > Ok, this is another patch. It changes a few things, and add support > for Nvidia Fakeraid through dm_nv module. Hi, This patch fix a serious bug in raid0 handling, also greatly simply raid0 code. Some adjustment and fix as well. -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_5.diff --] [-- Type: text/x-diff; name=raid_5.diff, Size: 63134 bytes --] diff --git a/conf/common.mk b/conf/common.mk index 55cbf47..a2c4f22 100644 --- a/conf/common.mk +++ b/conf/common.mk @@ -190,12 +190,13 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c -CLEANFILES += grub-fstest$(EXEEXT) grub_fstest-util_grub_fstest.o grub_fstest-util_hostfs.o grub_fstest-util_misc.o grub_fstest-kern_file.o grub_fstest-kern_device.o grub_fstest-kern_disk.o grub_fstest-kern_err.o grub_fstest-kern_misc.o grub_fstest-disk_host.o grub_fstest-disk_loopback.o grub_fstest-normal_arg.o grub_fstest-normal_misc.o grub_fstest-io_gzio.o grub_fstest-lib_hexdump.o grub_fstest-commands_blocklist.o grub_fstest-commands_ls.o grub_fstest-fs_affs.o grub_fstest-fs_cpio.o grub_fstest-fs_ext2.o grub_fstest-fs_fat.o grub_fstest-fs_hfs.o grub_fstest-fs_hfsplus.o grub_fstest-fs_iso9660.o grub_fstest-fs_udf.o grub_fstest-fs_jfs.o grub_fstest-fs_minix.o grub_fstest-fs_ntfs.o grub_fstest-fs_ntfscomp.o grub_fstest-fs_reiserfs.o grub_fstest-fs_sfs.o grub_fstest-fs_ufs.o grub_fstest-fs_xfs.o grub_fstest-fs_afs.o grub_fstest-kern_partition.o grub_fstest-partmap_pc.o grub_fstest-partmap_apple.o grub_fstest-partmap_gpt.o grub_fstest-kern_fs.o grub_fstest-kern_env.o grub_fstest-fs_fshelp.o grub_fstest-disk_lvm.o grub_fstest-disk_raid.o grub_fstest-grub_fstest_init.o -MOSTLYCLEANFILES += grub_fstest-util_grub_fstest.d grub_fstest-util_hostfs.d grub_fstest-util_misc.d grub_fstest-kern_file.d grub_fstest-kern_device.d grub_fstest-kern_disk.d grub_fstest-kern_err.d grub_fstest-kern_misc.d grub_fstest-disk_host.d grub_fstest-disk_loopback.d grub_fstest-normal_arg.d grub_fstest-normal_misc.d grub_fstest-io_gzio.d grub_fstest-lib_hexdump.d grub_fstest-commands_blocklist.d grub_fstest-commands_ls.d grub_fstest-fs_affs.d grub_fstest-fs_cpio.d grub_fstest-fs_ext2.d grub_fstest-fs_fat.d grub_fstest-fs_hfs.d grub_fstest-fs_hfsplus.d grub_fstest-fs_iso9660.d grub_fstest-fs_udf.d grub_fstest-fs_jfs.d grub_fstest-fs_minix.d grub_fstest-fs_ntfs.d grub_fstest-fs_ntfscomp.d grub_fstest-fs_reiserfs.d grub_fstest-fs_sfs.d grub_fstest-fs_ufs.d grub_fstest-fs_xfs.d grub_fstest-fs_afs.d grub_fstest-kern_partition.d grub_fstest-partmap_pc.d grub_fstest-partmap_apple.d grub_fstest-partmap_gpt.d grub_fstest-kern_fs.d grub_fstest-kern_env.d grub_fstest-fs_fshelp.d grub_fstest-disk_lvm.d grub_fstest-disk_raid.d grub_fstest-grub_fstest_init.d +CLEANFILES += grub-fstest$(EXEEXT) grub_fstest-util_grub_fstest.o grub_fstest-util_hostfs.o grub_fstest-util_misc.o grub_fstest-kern_file.o grub_fstest-kern_device.o grub_fstest-kern_disk.o grub_fstest-kern_err.o grub_fstest-kern_misc.o grub_fstest-disk_host.o grub_fstest-disk_loopback.o grub_fstest-normal_arg.o grub_fstest-normal_misc.o grub_fstest-io_gzio.o grub_fstest-lib_hexdump.o grub_fstest-commands_blocklist.o grub_fstest-commands_ls.o grub_fstest-fs_affs.o grub_fstest-fs_cpio.o grub_fstest-fs_ext2.o grub_fstest-fs_fat.o grub_fstest-fs_hfs.o grub_fstest-fs_hfsplus.o grub_fstest-fs_iso9660.o grub_fstest-fs_udf.o grub_fstest-fs_jfs.o grub_fstest-fs_minix.o grub_fstest-fs_ntfs.o grub_fstest-fs_ntfscomp.o grub_fstest-fs_reiserfs.o grub_fstest-fs_sfs.o grub_fstest-fs_ufs.o grub_fstest-fs_xfs.o grub_fstest-fs_afs.o grub_fstest-kern_partition.o grub_fstest-partmap_pc.o grub_fstest-partmap_apple.o grub_fstest-partmap_gpt.o grub_fstest-kern_fs.o grub_fstest-kern_env.o grub_fstest-fs_fshelp.o grub_fstest-disk_lvm.o grub_fstest-disk_raid.o grub_fstest-disk_mdraid_linux.o grub_fstest-disk_dmraid_nvidia.o grub_fstest-grub_fstest_init.o +MOSTLYCLEANFILES += grub_fstest-util_grub_fstest.d grub_fstest-util_hostfs.d grub_fstest-util_misc.d grub_fstest-kern_file.d grub_fstest-kern_device.d grub_fstest-kern_disk.d grub_fstest-kern_err.d grub_fstest-kern_misc.d grub_fstest-disk_host.d grub_fstest-disk_loopback.d grub_fstest-normal_arg.d grub_fstest-normal_misc.d grub_fstest-io_gzio.d grub_fstest-lib_hexdump.d grub_fstest-commands_blocklist.d grub_fstest-commands_ls.d grub_fstest-fs_affs.d grub_fstest-fs_cpio.d grub_fstest-fs_ext2.d grub_fstest-fs_fat.d grub_fstest-fs_hfs.d grub_fstest-fs_hfsplus.d grub_fstest-fs_iso9660.d grub_fstest-fs_udf.d grub_fstest-fs_jfs.d grub_fstest-fs_minix.d grub_fstest-fs_ntfs.d grub_fstest-fs_ntfscomp.d grub_fstest-fs_reiserfs.d grub_fstest-fs_sfs.d grub_fstest-fs_ufs.d grub_fstest-fs_xfs.d grub_fstest-fs_afs.d grub_fstest-kern_partition.d grub_fstest-partmap_pc.d grub_fstest-partmap_apple.d grub_fstest-partmap_gpt.d grub_fstest-kern_fs.d grub_fstest-kern_env.d grub_fstest-fs_fshelp.d grub_fstest-disk_lvm.d grub_fstest-disk_raid.d grub_fstest-disk_mdraid_linux.d grub_fstest-disk_dmraid_nvidia.d grub_fstest-grub_fstest_init.d -grub-fstest: $(grub_fstest_DEPENDENCIES) grub_fstest-util_grub_fstest.o grub_fstest-util_hostfs.o grub_fstest-util_misc.o grub_fstest-kern_file.o grub_fstest-kern_device.o grub_fstest-kern_disk.o grub_fstest-kern_err.o grub_fstest-kern_misc.o grub_fstest-disk_host.o grub_fstest-disk_loopback.o grub_fstest-normal_arg.o grub_fstest-normal_misc.o grub_fstest-io_gzio.o grub_fstest-lib_hexdump.o grub_fstest-commands_blocklist.o grub_fstest-commands_ls.o grub_fstest-fs_affs.o grub_fstest-fs_cpio.o grub_fstest-fs_ext2.o grub_fstest-fs_fat.o grub_fstest-fs_hfs.o grub_fstest-fs_hfsplus.o grub_fstest-fs_iso9660.o grub_fstest-fs_udf.o grub_fstest-fs_jfs.o grub_fstest-fs_minix.o grub_fstest-fs_ntfs.o grub_fstest-fs_ntfscomp.o grub_fstest-fs_reiserfs.o grub_fstest-fs_sfs.o grub_fstest-fs_ufs.o grub_fstest-fs_xfs.o grub_fstest-fs_afs.o grub_fstest-kern_partition.o grub_fstest-partmap_pc.o grub_fstest-partmap_apple.o grub_fstest-partmap_gpt.o grub_fstest-kern_fs.o grub_fstest-kern_env.o grub_fstest-fs_fshelp.o grub_fstest-disk_lvm.o grub_fstest-disk_raid.o grub_fstest-grub_fstest_init.o - $(CC) -o $@ grub_fstest-util_grub_fstest.o grub_fstest-util_hostfs.o grub_fstest-util_misc.o grub_fstest-kern_file.o grub_fstest-kern_device.o grub_fstest-kern_disk.o grub_fstest-kern_err.o grub_fstest-kern_misc.o grub_fstest-disk_host.o grub_fstest-disk_loopback.o grub_fstest-normal_arg.o grub_fstest-normal_misc.o grub_fstest-io_gzio.o grub_fstest-lib_hexdump.o grub_fstest-commands_blocklist.o grub_fstest-commands_ls.o grub_fstest-fs_affs.o grub_fstest-fs_cpio.o grub_fstest-fs_ext2.o grub_fstest-fs_fat.o grub_fstest-fs_hfs.o grub_fstest-fs_hfsplus.o grub_fstest-fs_iso9660.o grub_fstest-fs_udf.o grub_fstest-fs_jfs.o grub_fstest-fs_minix.o grub_fstest-fs_ntfs.o grub_fstest-fs_ntfscomp.o grub_fstest-fs_reiserfs.o grub_fstest-fs_sfs.o grub_fstest-fs_ufs.o grub_fstest-fs_xfs.o grub_fstest-fs_afs.o grub_fstest-kern_partition.o grub_fstest-partmap_pc.o grub_fstest-partmap_apple.o grub_fstest-partmap_gpt.o grub_fstest-kern_fs.o grub_fstest-kern_env.o grub_fstest-fs_fshelp.o grub_fstest-disk_lvm.o grub_fstest-disk_raid.o grub_fstest-grub_fstest_init.o $(LDFLAGS) $(grub_fstest_LDFLAGS) +grub-fstest: $(grub_fstest_DEPENDENCIES) grub_fstest-util_grub_fstest.o grub_fstest-util_hostfs.o grub_fstest-util_misc.o grub_fstest-kern_file.o grub_fstest-kern_device.o grub_fstest-kern_disk.o grub_fstest-kern_err.o grub_fstest-kern_misc.o grub_fstest-disk_host.o grub_fstest-disk_loopback.o grub_fstest-normal_arg.o grub_fstest-normal_misc.o grub_fstest-io_gzio.o grub_fstest-lib_hexdump.o grub_fstest-commands_blocklist.o grub_fstest-commands_ls.o grub_fstest-fs_affs.o grub_fstest-fs_cpio.o grub_fstest-fs_ext2.o grub_fstest-fs_fat.o grub_fstest-fs_hfs.o grub_fstest-fs_hfsplus.o grub_fstest-fs_iso9660.o grub_fstest-fs_udf.o grub_fstest-fs_jfs.o grub_fstest-fs_minix.o grub_fstest-fs_ntfs.o grub_fstest-fs_ntfscomp.o grub_fstest-fs_reiserfs.o grub_fstest-fs_sfs.o grub_fstest-fs_ufs.o grub_fstest-fs_xfs.o grub_fstest-fs_afs.o grub_fstest-kern_partition.o grub_fstest-partmap_pc.o grub_fstest-partmap_apple.o grub_fstest-partmap_gpt.o grub_fstest-kern_fs.o grub_fstest-kern_env.o grub_fstest-fs_fshelp.o grub_fstest-disk_lvm.o grub_fstest-disk_raid.o grub_fstest-disk_mdraid_linux.o grub_fstest-disk_dmraid_nvidia.o grub_fstest-grub_fstest_init.o + $(CC) -o $@ grub_fstest-util_grub_fstest.o grub_fstest-util_hostfs.o grub_fstest-util_misc.o grub_fstest-kern_file.o grub_fstest-kern_device.o grub_fstest-kern_disk.o grub_fstest-kern_err.o grub_fstest-kern_misc.o grub_fstest-disk_host.o grub_fstest-disk_loopback.o grub_fstest-normal_arg.o grub_fstest-normal_misc.o grub_fstest-io_gzio.o grub_fstest-lib_hexdump.o grub_fstest-commands_blocklist.o grub_fstest-commands_ls.o grub_fstest-fs_affs.o grub_fstest-fs_cpio.o grub_fstest-fs_ext2.o grub_fstest-fs_fat.o grub_fstest-fs_hfs.o grub_fstest-fs_hfsplus.o grub_fstest-fs_iso9660.o grub_fstest-fs_udf.o grub_fstest-fs_jfs.o grub_fstest-fs_minix.o grub_fstest-fs_ntfs.o grub_fstest-fs_ntfscomp.o grub_fstest-fs_reiserfs.o grub_fstest-fs_sfs.o grub_fstest-fs_ufs.o grub_fstest-fs_xfs.o grub_fstest-fs_afs.o grub_fstest-kern_partition.o grub_fstest-partmap_pc.o grub_fstest-partmap_apple.o grub_fstest-partmap_gpt.o grub_fstest-kern_fs.o grub_fstest-kern_env.o grub_fstest-fs_fshelp.o grub_fstest-disk_lvm.o grub_fstest-disk_raid.o grub_fstest-disk_mdraid_linux.o grub_fstest-disk_dmraid_nvidia.o grub_fstest-grub_fstest_init.o $(LDFLAGS) $(grub_fstest_LDFLAGS) grub_fstest-util_grub_fstest.o: util/grub-fstest.c $(util/grub-fstest.c_DEPENDENCIES) $(CC) -Iutil -I$(srcdir)/util $(CPPFLAGS) $(CFLAGS) -DGRUB_UTIL=1 $(grub_fstest_CFLAGS) -MD -c -o $@ $< @@ -365,6 +366,14 @@ grub_fstest-disk_raid.o: disk/raid.c $(disk/raid.c_DEPENDENCIES) $(CC) -Idisk -I$(srcdir)/disk $(CPPFLAGS) $(CFLAGS) -DGRUB_UTIL=1 $(grub_fstest_CFLAGS) -MD -c -o $@ $< -include grub_fstest-disk_raid.d +grub_fstest-disk_mdraid_linux.o: disk/mdraid_linux.c $(disk/mdraid_linux.c_DEPENDENCIES) + $(CC) -Idisk -I$(srcdir)/disk $(CPPFLAGS) $(CFLAGS) -DGRUB_UTIL=1 $(grub_fstest_CFLAGS) -MD -c -o $@ $< +-include grub_fstest-disk_mdraid_linux.d + +grub_fstest-disk_dmraid_nvidia.o: disk/dmraid_nvidia.c $(disk/dmraid_nvidia.c_DEPENDENCIES) + $(CC) -Idisk -I$(srcdir)/disk $(CPPFLAGS) $(CFLAGS) -DGRUB_UTIL=1 $(grub_fstest_CFLAGS) -MD -c -o $@ $< +-include grub_fstest-disk_dmraid_nvidia.d + grub_fstest-grub_fstest_init.o: grub_fstest_init.c $(grub_fstest_init.c_DEPENDENCIES) $(CC) -I. -I$(srcdir)/. $(CPPFLAGS) $(CFLAGS) -DGRUB_UTIL=1 $(grub_fstest_CFLAGS) -MD -c -o $@ $< -include grub_fstest-grub_fstest_init.d @@ -1885,7 +1894,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod lvm.mod mdraid.mod dm_nv.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -2001,6 +2010,120 @@ partmap-lvm_mod-disk_lvm.lst: disk/lvm.c $(disk/lvm.c_DEPENDENCIES) genpartmapli lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +CLEANFILES += mdraid.mod mod-mdraid.o mod-mdraid.c pre-mdraid.o mdraid_mod-disk_mdraid_linux.o und-mdraid.lst +ifneq ($(mdraid_mod_EXPORTS),no) +CLEANFILES += def-mdraid.lst +DEFSYMFILES += def-mdraid.lst +endif +MOSTLYCLEANFILES += mdraid_mod-disk_mdraid_linux.d +UNDSYMFILES += und-mdraid.lst + +mdraid.mod: pre-mdraid.o mod-mdraid.o $(TARGET_OBJ2ELF) + -rm -f $@ + $(TARGET_CC) $(mdraid_mod_LDFLAGS) $(TARGET_LDFLAGS) $(MODULE_LDFLAGS) -Wl,-r,-d -o $@ pre-mdraid.o mod-mdraid.o + if test ! -z $(TARGET_OBJ2ELF); then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@ + +pre-mdraid.o: $(mdraid_mod_DEPENDENCIES) mdraid_mod-disk_mdraid_linux.o + -rm -f $@ + $(TARGET_CC) $(mdraid_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ mdraid_mod-disk_mdraid_linux.o + +mod-mdraid.o: mod-mdraid.c + $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(mdraid_mod_CFLAGS) -c -o $@ $< + +mod-mdraid.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'mdraid' $< > $@ || (rm -f $@; exit 1) + +ifneq ($(mdraid_mod_EXPORTS),no) +def-mdraid.lst: pre-mdraid.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 mdraid/' > $@ +endif + +und-mdraid.lst: pre-mdraid.o + echo 'mdraid' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +mdraid_mod-disk_mdraid_linux.o: disk/mdraid_linux.c $(disk/mdraid_linux.c_DEPENDENCIES) + $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(mdraid_mod_CFLAGS) -MD -c -o $@ $< +-include mdraid_mod-disk_mdraid_linux.d + +CLEANFILES += cmd-mdraid_mod-disk_mdraid_linux.lst fs-mdraid_mod-disk_mdraid_linux.lst partmap-mdraid_mod-disk_mdraid_linux.lst +COMMANDFILES += cmd-mdraid_mod-disk_mdraid_linux.lst +FSFILES += fs-mdraid_mod-disk_mdraid_linux.lst +PARTMAPFILES += partmap-mdraid_mod-disk_mdraid_linux.lst + +cmd-mdraid_mod-disk_mdraid_linux.lst: disk/mdraid_linux.c $(disk/mdraid_linux.c_DEPENDENCIES) gencmdlist.sh + set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(mdraid_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh mdraid > $@ || (rm -f $@; exit 1) + +fs-mdraid_mod-disk_mdraid_linux.lst: disk/mdraid_linux.c $(disk/mdraid_linux.c_DEPENDENCIES) genfslist.sh + set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(mdraid_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh mdraid > $@ || (rm -f $@; exit 1) + +partmap-mdraid_mod-disk_mdraid_linux.lst: disk/mdraid_linux.c $(disk/mdraid_linux.c_DEPENDENCIES) genpartmaplist.sh + set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(mdraid_mod_CFLAGS) -E $< | sh $(srcdir)/genpartmaplist.sh mdraid > $@ || (rm -f $@; exit 1) + + +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +CLEANFILES += dm_nv.mod mod-dm_nv.o mod-dm_nv.c pre-dm_nv.o dm_nv_mod-disk_dmraid_nvidia.o und-dm_nv.lst +ifneq ($(dm_nv_mod_EXPORTS),no) +CLEANFILES += def-dm_nv.lst +DEFSYMFILES += def-dm_nv.lst +endif +MOSTLYCLEANFILES += dm_nv_mod-disk_dmraid_nvidia.d +UNDSYMFILES += und-dm_nv.lst + +dm_nv.mod: pre-dm_nv.o mod-dm_nv.o $(TARGET_OBJ2ELF) + -rm -f $@ + $(TARGET_CC) $(dm_nv_mod_LDFLAGS) $(TARGET_LDFLAGS) $(MODULE_LDFLAGS) -Wl,-r,-d -o $@ pre-dm_nv.o mod-dm_nv.o + if test ! -z $(TARGET_OBJ2ELF); then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi + $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@ + +pre-dm_nv.o: $(dm_nv_mod_DEPENDENCIES) dm_nv_mod-disk_dmraid_nvidia.o + -rm -f $@ + $(TARGET_CC) $(dm_nv_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ dm_nv_mod-disk_dmraid_nvidia.o + +mod-dm_nv.o: mod-dm_nv.c + $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(dm_nv_mod_CFLAGS) -c -o $@ $< + +mod-dm_nv.c: moddep.lst genmodsrc.sh + sh $(srcdir)/genmodsrc.sh 'dm_nv' $< > $@ || (rm -f $@; exit 1) + +ifneq ($(dm_nv_mod_EXPORTS),no) +def-dm_nv.lst: pre-dm_nv.o + $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 dm_nv/' > $@ +endif + +und-dm_nv.lst: pre-dm_nv.o + echo 'dm_nv' > $@ + $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ + +dm_nv_mod-disk_dmraid_nvidia.o: disk/dmraid_nvidia.c $(disk/dmraid_nvidia.c_DEPENDENCIES) + $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(dm_nv_mod_CFLAGS) -MD -c -o $@ $< +-include dm_nv_mod-disk_dmraid_nvidia.d + +CLEANFILES += cmd-dm_nv_mod-disk_dmraid_nvidia.lst fs-dm_nv_mod-disk_dmraid_nvidia.lst partmap-dm_nv_mod-disk_dmraid_nvidia.lst +COMMANDFILES += cmd-dm_nv_mod-disk_dmraid_nvidia.lst +FSFILES += fs-dm_nv_mod-disk_dmraid_nvidia.lst +PARTMAPFILES += partmap-dm_nv_mod-disk_dmraid_nvidia.lst + +cmd-dm_nv_mod-disk_dmraid_nvidia.lst: disk/dmraid_nvidia.c $(disk/dmraid_nvidia.c_DEPENDENCIES) gencmdlist.sh + set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(dm_nv_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh dm_nv > $@ || (rm -f $@; exit 1) + +fs-dm_nv_mod-disk_dmraid_nvidia.lst: disk/dmraid_nvidia.c $(disk/dmraid_nvidia.c_DEPENDENCIES) genfslist.sh + set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(dm_nv_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh dm_nv > $@ || (rm -f $@; exit 1) + +partmap-dm_nv_mod-disk_dmraid_nvidia.lst: disk/dmraid_nvidia.c $(disk/dmraid_nvidia.c_DEPENDENCIES) genpartmaplist.sh + set -e; $(TARGET_CC) -Idisk -I$(srcdir)/disk $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(dm_nv_mod_CFLAGS) -E $< | sh $(srcdir)/genpartmaplist.sh dm_nv > $@ || (rm -f $@; exit 1) + + +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/conf/common.rmk b/conf/common.rmk index 95859f7..3876ce4 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -15,7 +15,7 @@ grub_probe_SOURCES = util/grub-probe.c \ \ partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c \ - disk/lvm.c disk/raid.c grub_probe_init.c + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c ifeq ($(enable_grub_fstest), yes) bin_UTILITIES += grub-fstest @@ -35,6 +35,7 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c # For the parser. @@ -264,7 +265,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod lvm.mod mdraid.mod dm_nv.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -276,6 +277,16 @@ lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c new file mode 100644 index 0000000..557edcb --- /dev/null +++ b/disk/dmraid_nvidia.c @@ -0,0 +1,160 @@ +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +#define NV_SIGNATURES 4 + +#define NV_IDLE 0 +#define NV_SCDB_INIT_RAID 2 +#define NV_SCDB_REBUILD_RAID 3 +#define NV_SCDB_UPGRADE_RAID 4 +#define NV_SCDB_SYNC_RAID 5 + +#define NV_LEVEL_UNKNOWN 0x00 +#define NV_LEVEL_JBOD 0xFF +#define NV_LEVEL_0 0x80 +#define NV_LEVEL_1 0x81 +#define NV_LEVEL_3 0x83 +#define NV_LEVEL_5 0x85 +#define NV_LEVEL_10 0x8a +#define NV_LEVEL_1_0 0x8180 + +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ + +struct grub_raid_nv_array +{ + grub_uint32_t version; + grub_uint32_t signature[NV_SIGNATURES]; + grub_uint8_t raid_job_code; + grub_uint8_t stripe_width; + grub_uint8_t total_volumes; + grub_uint8_t original_width; + grub_uint32_t raid_level; + grub_uint32_t stripe_block_size; + grub_uint32_t stripe_block_size_bytes; + grub_uint32_t stripe_block_size_log2; + grub_uint32_t stripe_mask; + grub_uint32_t stripe_size; + grub_uint32_t stripe_size_bytes; + grub_uint32_t raid_job_mask; + grub_uint32_t original_capacity; + grub_uint32_t flags; +}; + +#define NV_ID_LENGTH 8 +#define NV_ID_STRING "NVIDIA" +#define NV_VERSION 100 + +#define NV_PRODUCTIDS 16 +#define NV_PRODUCTREVISIONS 4 + +struct grub_raid_nv_super +{ + grub_uint8_t vendor[NV_ID_LENGTH]; /* 0x00 - 0x07 ID string. */ + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ + grub_uint8_t reserved; /* 0x13. */ + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ + grub_uint8_t product_id[NV_PRODUCTIDS]; /* 0x1C - 0x2B Array product ID. */ + grub_uint8_t product_rev[NV_PRODUCTREVISIONS]; /* 0x2C - 0x2F Array product revision */ + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ + struct grub_raid_nv_array array; /* Array information */ +} __attribute__ ((packed)); + +static grub_err_t +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + struct grub_raid_nv_super sb; + grub_uint32_t *uuid; + + if (disk->partition) + return grub_error (GRUB_ERR_BAD_DEVICE, "don't scan partition"); + + sector = grub_disk_get_size (disk) - 2; + + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) + return grub_errno; + + if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) + return grub_error (GRUB_ERR_BAD_DEVICE, "not raid"); + + if (sb.version != NV_VERSION) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unknown version: %d.%d", sb.version); + + switch (sb.array.raid_level) + { + case NV_LEVEL_0: + array->level = 0; + array->disk_size = sb.capacity / sb.array.total_volumes; + break; + + case NV_LEVEL_1: + array->level = 1; + array->disk_size = sb.capacity; + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.array.raid_level); + } + + array->number = 0; + array->total_devs = sb.array.total_volumes; + array->chunk_size = sb.array.stripe_size; + array->index = sb.unit_number; + array->uuid_len = sizeof (sb.array.signature); + array->uuid = grub_malloc (sizeof (sb.array.signature)); + if (! array->uuid) + return grub_errno; + + grub_memcpy (array->uuid, (char *) &sb.array.signature, + sizeof (sb.array.signature)); + + return 0; +} + +static struct grub_raid grub_dmraid_nv_dev = +{ + .name = "dmraid_nv", + .detect = grub_dmraid_nv_detect, + .next = 0 +}; + +GRUB_MOD_INIT(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} + +GRUB_MOD_FINI(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..8b7f5ed 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; - total_sectors = 9000000; /* TODO: get the correct size. */ + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c new file mode 100644 index 0000000..de36d7b --- /dev/null +++ b/disk/mdraid_linux.c @@ -0,0 +1,244 @@ +/* mdraid_linux.c - module to handle linux softraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define GRUB_RAID_RESERVED_BYTES (64 * 1024) +#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) + +#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ + - GRUB_RAID_RESERVED_SECTORS) + +#define GRUB_RAID_SB_BYTES 4096 +#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) +#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define GRUB_RAID_SB_GENERIC_OFFSET 0 + +#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 +#define GRUB_RAID_SB_DISKS_OFFSET 128 +#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 + +#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 +#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 +#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ + + GRUB_RAID_SB_GENERIC_STATE_WORDS) +#define GRUB_RAID_SB_PERSONALITY_WORDS 64 +#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 +#define GRUB_RAID_SB_DISKS 27 +#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ + - GRUB_RAID_SB_PERSONALITY_WORDS \ + - GRUB_RAID_SB_DISKS_WORDS \ + - GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ + + GRUB_RAID_SB_PERSONALITY_WORDS \ + + GRUB_RAID_SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ +#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ +#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ +#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + +#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. + * read requests will only be sent here in + * dire need + */ + + +#define GRUB_RAID_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define GRUB_RAID_SB_CLEAN 0 +#define GRUB_RAID_SB_ERRORS 1 + +#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + +struct grub_raid_disk_09 { + grub_uint32_t number; /* 0 Device number in the entire set */ + grub_uint32_t major; /* 1 Device major number */ + grub_uint32_t minor; /* 2 Device minor number */ + grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ + grub_uint32_t state; /* 4 Operational state */ + grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 { + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* 0 MD identifier */ + grub_uint32_t major_version; /* 1 major version to which the set conforms */ + grub_uint32_t minor_version; /* 2 minor version ... */ + grub_uint32_t patch_version; /* 3 patchlevel version ... */ + grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ + grub_uint32_t set_uuid0; /* 5 Raid set identifier */ + grub_uint32_t ctime; /* 6 Creation time */ + grub_uint32_t level; /* 7 Raid personality */ + grub_uint32_t size; /* 8 Apparent size of each individual disk */ + grub_uint32_t nr_disks; /* 9 total disks in the raid set */ + grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ + grub_uint32_t md_minor; /* 11 preferred MD minor device number */ + grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ + grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ + grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ + grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ + grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* 0 Superblock update time */ + grub_uint32_t state; /* 1 State bits (clean, ...) */ + grub_uint32_t active_disks; /* 2 Number of currently active disks */ + grub_uint32_t working_disks; /* 3 Number of working disks */ + grub_uint32_t failed_disks; /* 4 Number of failed disks */ + grub_uint32_t spare_disks; /* 5 Number of spare disks */ + grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ +#ifdef GRUB_HOST_WORDS_BIGENDIAN + grub_uint32_t events_hi; /* 7 high-order of superblock update count */ + grub_uint32_t events_lo; /* 8 low-order of superblock update count */ + grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ + grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ +#else + grub_uint32_t events_lo; /* 7 low-order of superblock update count */ + grub_uint32_t events_hi; /* 8 high-order of superblock update count */ + grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ + grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ +#endif + grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ + grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* 0 the array's physical layout */ + grub_uint32_t chunk_size; /* 1 chunk size in bytes */ + grub_uint32_t root_pv; /* 2 LV root PV */ + grub_uint32_t root_block; /* 3 LV root block */ + grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +}; + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = GRUB_RAID_NEW_SIZE_SECTORS(size); + + if (grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != GRUB_RAID_SB_MAGIC) + return grub_error (GRUB_ERR_BAD_DEVICE, "not mdraid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + /* FIXME: Support all RAID levels. */ + if (sb.level != 0 && sb.level != 1 && sb.level != 5) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", + sb.level); + + /* FIXME: Support all layouts. */ + if (sb.level == 5 && sb.layout != 2) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID5 layout: %d", + sb.layout); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = 0; + array->total_devs = sb.nr_disks; + array->disk_size = (sb.size) ? sb.size * 2 : sector; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (! array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = +{ + .name = "mdraid", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} + +GRUB_MOD_FINI(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 731bf1f..521f1ac 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -112,9 +112,15 @@ grub_raid_open (const char *name, grub_disk_t disk) switch (array->level) { case 0: - /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; - break; + { + unsigned i; + + disk->total_sectors = 0; + for (i = 0; i < array->total_devs; i++) + disk->total_sectors += array->device[i]->total_sectors; + + break; + } case 1: disk->total_sectors = array->disk_size; @@ -152,56 +158,39 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, grub_uint32_t b; unsigned int disknr; grub_disk_addr_t read_sector; - grub_size_t read_size; /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, array->total_devs, &disknr); - - a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; - - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; + a = grub_divmod64 (sector, array->chunk_size, &b); + a = grub_divmod64 (a, array->total_devs, &disknr); + read_sector = a * array->chunk_size; while (1) { - grub_uint32_t i; + grub_size_t read_size; - err = grub_disk_read (array->device[disknr], read_sector, 0, + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + err = grub_disk_read (array->device[disknr], read_sector + b, 0, read_size << GRUB_DISK_SECTOR_BITS, buf); if (err) break; - buf += read_size; + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - + b = 0; disknr++; /* See whether the disk was the last disk, and start reading from the first disk in that case. */ if (disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - } + { + read_sector += array->chunk_size; + disknr = 0; + } } } break; @@ -348,169 +337,103 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *scanner_name) { - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); - - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } - - /* FIXME: Check the checksum. */ - - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } - - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } array->name = grub_malloc (13); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } grub_sprintf (array->name, "md%d", array->number); - grub_dprintf ("raid", "Found array: %s\n", array->name); + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, + scanner_name); /* Add our new array to the list. */ array->next = array_list; @@ -518,47 +441,115 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) + return 0; +} + + +static grub_raid_t grub_raid_list; + +static void +grub_raid_scan_device (int head_only) +{ + auto int hook (const char *name); + int hook (const char *name) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); + + disk = grub_disk_open (name); + if (!disk) + return 0; + + if ((disk->total_sectors == ULONG_MAX) || (disk->total_sectors == 0)) + { + grub_disk_close (disk); + return 0; + } + + for (p = grub_raid_list; p; p = p->next) + { + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + return 0; + + break; + } + + if (grub_errno != GRUB_ERR_BAD_DEVICE) + grub_print_error (); + + grub_errno = GRUB_ERR_NONE; + if (head_only) + break; + } + + grub_disk_close (disk); + + return 0; } - if (! array->device[sb.this_disk.number]) + grub_device_iterate (&hook); +} + +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + struct grub_raid_array *p; + int i; - return 0; + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); } - array->nr_devs++; + array_list = 0; +} - return 0; +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; + grub_raid_scan_device (1); +} + +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; + + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} + +void +grub_raid_rescan (void) +{ + free_array (); + grub_raid_scan_device (0); } static struct grub_disk_dev grub_raid_dev = @@ -579,12 +570,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..ca2edb7 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,43 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ int layout; /* Only for RAID 5. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) - -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) - -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ - -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ - - -#define GRUB_RAID_SB_MAGIC 0xa92b4efc - -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 +struct grub_raid +{ + const char *name; -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; + struct grub_raid *next; }; +typedef struct grub_raid *grub_raid_t; -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +void grub_raid_rescan (void); #endif /* ! GRUB_RAID_H */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..29234ac 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,6 +29,7 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> #include <grub_fstest_init.h> @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -273,32 +273,61 @@ cmd_hex (char *pathname) return 0; } - read_file (pathname, hex_hook); + if (pathname) + read_file (pathname, hex_hook); + else + { + char buf[BUF_SIZE]; + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device"); + + if (! leng) + leng = GRUB_DISK_SECTOR_SIZE; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d\n", + skip, len); + + hexdump (skip, buf, len); + + skip += len; + leng -= len; + } + + grub_device_close (dev); + } } static void -fstest (char *image_path, int cmd, int n, char **args) +fstest (char **images, int num_disks, int cmd, int n, char **args) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; - - - grub_sprintf (host_file, "(host)/%s", image_path); + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails.\n"); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) cmd_cmp (args[0], args[1]); break; case CMD_HEX: - cmd_hex (args[0]); + cmd_hex ((n == 0) ? 0 : args[0]); break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ ls PATH list files in PATH\n\ cp SRC DEST copy file to local system\n\ cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + hex [FILE] Hex dump FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,35 +481,29 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) - { - fprintf (stderr, "No path is specified.\n"); - usage (1); - } - - image_path = argv[optind]; - - if (*image_path != '/') + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "Must use absolute path.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; @@ -478,6 +521,7 @@ main (int argc, char *argv[]) else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +547,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-11 18:45 ` Bean @ 2008-08-11 20:13 ` Bean 2008-08-11 20:24 ` Bean 0 siblings, 1 reply; 24+ messages in thread From: Bean @ 2008-08-11 20:13 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 443 bytes --] On Tue, Aug 12, 2008 at 2:45 AM, Bean <bean123ch@gmail.com> wrote: > On Sun, Aug 10, 2008 at 11:49 PM, Bean <bean123ch@gmail.com> wrote: >> Hi, >> >> Ok, this is another patch. It changes a few things, and add support >> for Nvidia Fakeraid through dm_nv module. > > Hi, > > This patch fix a serious bug in raid0 handling, also greatly simply > raid0 code. Some adjustment and fix as well. Hi, New patch that fix dmraid_nvidia.c. -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_5.diff --] [-- Type: text/x-diff; name=raid_5.diff, Size: 46370 bytes --] diff --git a/conf/common.rmk b/conf/common.rmk index 95859f7..3876ce4 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -15,7 +15,7 @@ grub_probe_SOURCES = util/grub-probe.c \ \ partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c \ - disk/lvm.c disk/raid.c grub_probe_init.c + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c ifeq ($(enable_grub_fstest), yes) bin_UTILITIES += grub-fstest @@ -35,6 +35,7 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c # For the parser. @@ -264,7 +265,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod lvm.mod mdraid.mod dm_nv.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -276,6 +277,16 @@ lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c new file mode 100644 index 0000000..0c742fa --- /dev/null +++ b/disk/dmraid_nvidia.c @@ -0,0 +1,160 @@ +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +#define NV_SIGNATURES 4 + +#define NV_IDLE 0 +#define NV_SCDB_INIT_RAID 2 +#define NV_SCDB_REBUILD_RAID 3 +#define NV_SCDB_UPGRADE_RAID 4 +#define NV_SCDB_SYNC_RAID 5 + +#define NV_LEVEL_UNKNOWN 0x00 +#define NV_LEVEL_JBOD 0xFF +#define NV_LEVEL_0 0x80 +#define NV_LEVEL_1 0x81 +#define NV_LEVEL_3 0x83 +#define NV_LEVEL_5 0x85 +#define NV_LEVEL_10 0x8a +#define NV_LEVEL_1_0 0x8180 + +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ + +struct grub_raid_nv_array +{ + grub_uint32_t version; + grub_uint32_t signature[NV_SIGNATURES]; + grub_uint8_t raid_job_code; + grub_uint8_t stripe_width; + grub_uint8_t total_volumes; + grub_uint8_t original_width; + grub_uint32_t raid_level; + grub_uint32_t stripe_block_size; + grub_uint32_t stripe_block_size_bytes; + grub_uint32_t stripe_block_size_log2; + grub_uint32_t stripe_mask; + grub_uint32_t stripe_size; + grub_uint32_t stripe_size_bytes; + grub_uint32_t raid_job_mask; + grub_uint32_t original_capacity; + grub_uint32_t flags; +}; + +#define NV_ID_LENGTH 8 +#define NV_ID_STRING "NVIDIA" +#define NV_VERSION 100 + +#define NV_PRODUCTIDS 16 +#define NV_PRODUCTREVISIONS 4 + +struct grub_raid_nv_super +{ + grub_uint8_t vendor[NV_ID_LENGTH]; /* 0x00 - 0x07 ID string. */ + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ + grub_uint8_t reserved; /* 0x13. */ + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ + grub_uint8_t product_id[NV_PRODUCTIDS]; /* 0x1C - 0x2B Array product ID. */ + grub_uint8_t product_rev[NV_PRODUCTREVISIONS]; /* 0x2C - 0x2F Array product revision */ + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ + struct grub_raid_nv_array array; /* Array information */ +} __attribute__ ((packed)); + +static grub_err_t +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + struct grub_raid_nv_super sb; + grub_uint32_t *uuid; + + if (disk->partition) + return grub_error (GRUB_ERR_BAD_DEVICE, "don't scan partition"); + + sector = grub_disk_get_size (disk) - 2; + + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) + return grub_errno; + + if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) + return grub_error (GRUB_ERR_BAD_DEVICE, "not raid"); + + if (sb.version != NV_VERSION) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unknown version: %d.%d", sb.version); + + switch (sb.array.raid_level) + { + case NV_LEVEL_0: + array->level = 0; + array->disk_size = sb.capacity / sb.array.total_volumes; + break; + + case NV_LEVEL_1: + array->level = 1; + array->disk_size = sb.capacity; + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.array.raid_level); + } + + array->number = 0; + array->total_devs = sb.array.total_volumes; + array->chunk_size = sb.array.stripe_size / sb.array.total_volumes; + array->index = sb.unit_number; + array->uuid_len = sizeof (sb.array.signature); + array->uuid = grub_malloc (sizeof (sb.array.signature)); + if (! array->uuid) + return grub_errno; + + grub_memcpy (array->uuid, (char *) &sb.array.signature, + sizeof (sb.array.signature)); + + return 0; +} + +static struct grub_raid grub_dmraid_nv_dev = +{ + .name = "dmraid_nv", + .detect = grub_dmraid_nv_detect, + .next = 0 +}; + +GRUB_MOD_INIT(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} + +GRUB_MOD_FINI(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..8b7f5ed 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; - total_sectors = 9000000; /* TODO: get the correct size. */ + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c new file mode 100644 index 0000000..de36d7b --- /dev/null +++ b/disk/mdraid_linux.c @@ -0,0 +1,244 @@ +/* mdraid_linux.c - module to handle linux softraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define GRUB_RAID_RESERVED_BYTES (64 * 1024) +#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) + +#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ + - GRUB_RAID_RESERVED_SECTORS) + +#define GRUB_RAID_SB_BYTES 4096 +#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) +#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define GRUB_RAID_SB_GENERIC_OFFSET 0 + +#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 +#define GRUB_RAID_SB_DISKS_OFFSET 128 +#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 + +#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 +#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 +#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ + + GRUB_RAID_SB_GENERIC_STATE_WORDS) +#define GRUB_RAID_SB_PERSONALITY_WORDS 64 +#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 +#define GRUB_RAID_SB_DISKS 27 +#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ + - GRUB_RAID_SB_PERSONALITY_WORDS \ + - GRUB_RAID_SB_DISKS_WORDS \ + - GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ + + GRUB_RAID_SB_PERSONALITY_WORDS \ + + GRUB_RAID_SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ +#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ +#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ +#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + +#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. + * read requests will only be sent here in + * dire need + */ + + +#define GRUB_RAID_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define GRUB_RAID_SB_CLEAN 0 +#define GRUB_RAID_SB_ERRORS 1 + +#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + +struct grub_raid_disk_09 { + grub_uint32_t number; /* 0 Device number in the entire set */ + grub_uint32_t major; /* 1 Device major number */ + grub_uint32_t minor; /* 2 Device minor number */ + grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ + grub_uint32_t state; /* 4 Operational state */ + grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 { + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* 0 MD identifier */ + grub_uint32_t major_version; /* 1 major version to which the set conforms */ + grub_uint32_t minor_version; /* 2 minor version ... */ + grub_uint32_t patch_version; /* 3 patchlevel version ... */ + grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ + grub_uint32_t set_uuid0; /* 5 Raid set identifier */ + grub_uint32_t ctime; /* 6 Creation time */ + grub_uint32_t level; /* 7 Raid personality */ + grub_uint32_t size; /* 8 Apparent size of each individual disk */ + grub_uint32_t nr_disks; /* 9 total disks in the raid set */ + grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ + grub_uint32_t md_minor; /* 11 preferred MD minor device number */ + grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ + grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ + grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ + grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ + grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* 0 Superblock update time */ + grub_uint32_t state; /* 1 State bits (clean, ...) */ + grub_uint32_t active_disks; /* 2 Number of currently active disks */ + grub_uint32_t working_disks; /* 3 Number of working disks */ + grub_uint32_t failed_disks; /* 4 Number of failed disks */ + grub_uint32_t spare_disks; /* 5 Number of spare disks */ + grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ +#ifdef GRUB_HOST_WORDS_BIGENDIAN + grub_uint32_t events_hi; /* 7 high-order of superblock update count */ + grub_uint32_t events_lo; /* 8 low-order of superblock update count */ + grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ + grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ +#else + grub_uint32_t events_lo; /* 7 low-order of superblock update count */ + grub_uint32_t events_hi; /* 8 high-order of superblock update count */ + grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ + grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ +#endif + grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ + grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* 0 the array's physical layout */ + grub_uint32_t chunk_size; /* 1 chunk size in bytes */ + grub_uint32_t root_pv; /* 2 LV root PV */ + grub_uint32_t root_block; /* 3 LV root block */ + grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +}; + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = GRUB_RAID_NEW_SIZE_SECTORS(size); + + if (grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != GRUB_RAID_SB_MAGIC) + return grub_error (GRUB_ERR_BAD_DEVICE, "not mdraid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + /* FIXME: Support all RAID levels. */ + if (sb.level != 0 && sb.level != 1 && sb.level != 5) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", + sb.level); + + /* FIXME: Support all layouts. */ + if (sb.level == 5 && sb.layout != 2) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID5 layout: %d", + sb.layout); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = 0; + array->total_devs = sb.nr_disks; + array->disk_size = (sb.size) ? sb.size * 2 : sector; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (! array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = +{ + .name = "mdraid", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} + +GRUB_MOD_FINI(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 731bf1f..521f1ac 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -112,9 +112,15 @@ grub_raid_open (const char *name, grub_disk_t disk) switch (array->level) { case 0: - /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; - break; + { + unsigned i; + + disk->total_sectors = 0; + for (i = 0; i < array->total_devs; i++) + disk->total_sectors += array->device[i]->total_sectors; + + break; + } case 1: disk->total_sectors = array->disk_size; @@ -152,56 +158,39 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, grub_uint32_t b; unsigned int disknr; grub_disk_addr_t read_sector; - grub_size_t read_size; /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, array->total_devs, &disknr); - - a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; - - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; + a = grub_divmod64 (sector, array->chunk_size, &b); + a = grub_divmod64 (a, array->total_devs, &disknr); + read_sector = a * array->chunk_size; while (1) { - grub_uint32_t i; + grub_size_t read_size; - err = grub_disk_read (array->device[disknr], read_sector, 0, + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + err = grub_disk_read (array->device[disknr], read_sector + b, 0, read_size << GRUB_DISK_SECTOR_BITS, buf); if (err) break; - buf += read_size; + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - + b = 0; disknr++; /* See whether the disk was the last disk, and start reading from the first disk in that case. */ if (disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - } + { + read_sector += array->chunk_size; + disknr = 0; + } } } break; @@ -348,169 +337,103 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *scanner_name) { - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); - - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } - - /* FIXME: Check the checksum. */ - - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } - - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } array->name = grub_malloc (13); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } grub_sprintf (array->name, "md%d", array->number); - grub_dprintf ("raid", "Found array: %s\n", array->name); + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, + scanner_name); /* Add our new array to the list. */ array->next = array_list; @@ -518,47 +441,115 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) + return 0; +} + + +static grub_raid_t grub_raid_list; + +static void +grub_raid_scan_device (int head_only) +{ + auto int hook (const char *name); + int hook (const char *name) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); + + disk = grub_disk_open (name); + if (!disk) + return 0; + + if ((disk->total_sectors == ULONG_MAX) || (disk->total_sectors == 0)) + { + grub_disk_close (disk); + return 0; + } + + for (p = grub_raid_list; p; p = p->next) + { + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + return 0; + + break; + } + + if (grub_errno != GRUB_ERR_BAD_DEVICE) + grub_print_error (); + + grub_errno = GRUB_ERR_NONE; + if (head_only) + break; + } + + grub_disk_close (disk); + + return 0; } - if (! array->device[sb.this_disk.number]) + grub_device_iterate (&hook); +} + +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + struct grub_raid_array *p; + int i; - return 0; + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); } - array->nr_devs++; + array_list = 0; +} - return 0; +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; + grub_raid_scan_device (1); +} + +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; + + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} + +void +grub_raid_rescan (void) +{ + free_array (); + grub_raid_scan_device (0); } static struct grub_disk_dev grub_raid_dev = @@ -579,12 +570,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..ca2edb7 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,43 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ int layout; /* Only for RAID 5. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) - -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) - -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ - -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ - - -#define GRUB_RAID_SB_MAGIC 0xa92b4efc - -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 +struct grub_raid +{ + const char *name; -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; + struct grub_raid *next; }; +typedef struct grub_raid *grub_raid_t; -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +void grub_raid_rescan (void); #endif /* ! GRUB_RAID_H */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..29234ac 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,6 +29,7 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> #include <grub_fstest_init.h> @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -273,32 +273,61 @@ cmd_hex (char *pathname) return 0; } - read_file (pathname, hex_hook); + if (pathname) + read_file (pathname, hex_hook); + else + { + char buf[BUF_SIZE]; + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device"); + + if (! leng) + leng = GRUB_DISK_SECTOR_SIZE; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d\n", + skip, len); + + hexdump (skip, buf, len); + + skip += len; + leng -= len; + } + + grub_device_close (dev); + } } static void -fstest (char *image_path, int cmd, int n, char **args) +fstest (char **images, int num_disks, int cmd, int n, char **args) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; - - - grub_sprintf (host_file, "(host)/%s", image_path); + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails.\n"); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) cmd_cmp (args[0], args[1]); break; case CMD_HEX: - cmd_hex (args[0]); + cmd_hex ((n == 0) ? 0 : args[0]); break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ ls PATH list files in PATH\n\ cp SRC DEST copy file to local system\n\ cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + hex [FILE] Hex dump FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,35 +481,29 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) - { - fprintf (stderr, "No path is specified.\n"); - usage (1); - } - - image_path = argv[optind]; - - if (*image_path != '/') + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "Must use absolute path.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; @@ -478,6 +521,7 @@ main (int argc, char *argv[]) else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +547,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-11 20:13 ` Bean @ 2008-08-11 20:24 ` Bean 2008-08-11 20:30 ` Robert Millan 0 siblings, 1 reply; 24+ messages in thread From: Bean @ 2008-08-11 20:24 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 562 bytes --] On Tue, Aug 12, 2008 at 4:13 AM, Bean <bean123ch@gmail.com> wrote: > On Tue, Aug 12, 2008 at 2:45 AM, Bean <bean123ch@gmail.com> wrote: >> On Sun, Aug 10, 2008 at 11:49 PM, Bean <bean123ch@gmail.com> wrote: >>> Hi, >>> >>> Ok, this is another patch. It changes a few things, and add support >>> for Nvidia Fakeraid through dm_nv module. >> >> Hi, >> >> This patch fix a serious bug in raid0 handling, also greatly simply >> raid0 code. Some adjustment and fix as well. > > Hi, > > New patch that fix dmraid_nvidia.c. Hi, This one should really work. -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_5.diff --] [-- Type: text/x-diff; name=raid_5.diff, Size: 46351 bytes --] diff --git a/conf/common.rmk b/conf/common.rmk index 95859f7..3876ce4 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -15,7 +15,7 @@ grub_probe_SOURCES = util/grub-probe.c \ \ partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c \ - disk/lvm.c disk/raid.c grub_probe_init.c + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c ifeq ($(enable_grub_fstest), yes) bin_UTILITIES += grub-fstest @@ -35,6 +35,7 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c # For the parser. @@ -264,7 +265,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod lvm.mod mdraid.mod dm_nv.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -276,6 +277,16 @@ lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c new file mode 100644 index 0000000..01b3154 --- /dev/null +++ b/disk/dmraid_nvidia.c @@ -0,0 +1,160 @@ +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +#define NV_SIGNATURES 4 + +#define NV_IDLE 0 +#define NV_SCDB_INIT_RAID 2 +#define NV_SCDB_REBUILD_RAID 3 +#define NV_SCDB_UPGRADE_RAID 4 +#define NV_SCDB_SYNC_RAID 5 + +#define NV_LEVEL_UNKNOWN 0x00 +#define NV_LEVEL_JBOD 0xFF +#define NV_LEVEL_0 0x80 +#define NV_LEVEL_1 0x81 +#define NV_LEVEL_3 0x83 +#define NV_LEVEL_5 0x85 +#define NV_LEVEL_10 0x8a +#define NV_LEVEL_1_0 0x8180 + +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ + +struct grub_raid_nv_array +{ + grub_uint32_t version; + grub_uint32_t signature[NV_SIGNATURES]; + grub_uint8_t raid_job_code; + grub_uint8_t stripe_width; + grub_uint8_t total_volumes; + grub_uint8_t original_width; + grub_uint32_t raid_level; + grub_uint32_t stripe_block_size; + grub_uint32_t stripe_block_size_bytes; + grub_uint32_t stripe_block_size_log2; + grub_uint32_t stripe_mask; + grub_uint32_t stripe_size; + grub_uint32_t stripe_size_bytes; + grub_uint32_t raid_job_mask; + grub_uint32_t original_capacity; + grub_uint32_t flags; +}; + +#define NV_ID_LENGTH 8 +#define NV_ID_STRING "NVIDIA" +#define NV_VERSION 100 + +#define NV_PRODUCTIDS 16 +#define NV_PRODUCTREVISIONS 4 + +struct grub_raid_nv_super +{ + grub_uint8_t vendor[NV_ID_LENGTH]; /* 0x00 - 0x07 ID string. */ + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ + grub_uint8_t reserved; /* 0x13. */ + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ + grub_uint8_t product_id[NV_PRODUCTIDS]; /* 0x1C - 0x2B Array product ID. */ + grub_uint8_t product_rev[NV_PRODUCTREVISIONS]; /* 0x2C - 0x2F Array product revision */ + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ + struct grub_raid_nv_array array; /* Array information */ +} __attribute__ ((packed)); + +static grub_err_t +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + struct grub_raid_nv_super sb; + grub_uint32_t *uuid; + + if (disk->partition) + return grub_error (GRUB_ERR_BAD_DEVICE, "don't scan partition"); + + sector = grub_disk_get_size (disk) - 2; + + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) + return grub_errno; + + if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) + return grub_error (GRUB_ERR_BAD_DEVICE, "not raid"); + + if (sb.version != NV_VERSION) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unknown version: %d.%d", sb.version); + + switch (sb.array.raid_level) + { + case NV_LEVEL_0: + array->level = 0; + array->disk_size = sb.capacity / sb.array.total_volumes; + break; + + case NV_LEVEL_1: + array->level = 1; + array->disk_size = sb.capacity; + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.array.raid_level); + } + + array->number = 0; + array->total_devs = sb.array.total_volumes; + array->chunk_size = sb.array.stripe_block_size; + array->index = sb.unit_number; + array->uuid_len = sizeof (sb.array.signature); + array->uuid = grub_malloc (sizeof (sb.array.signature)); + if (! array->uuid) + return grub_errno; + + grub_memcpy (array->uuid, (char *) &sb.array.signature, + sizeof (sb.array.signature)); + + return 0; +} + +static struct grub_raid grub_dmraid_nv_dev = +{ + .name = "dmraid_nv", + .detect = grub_dmraid_nv_detect, + .next = 0 +}; + +GRUB_MOD_INIT(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} + +GRUB_MOD_FINI(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..8b7f5ed 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; - total_sectors = 9000000; /* TODO: get the correct size. */ + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c new file mode 100644 index 0000000..de36d7b --- /dev/null +++ b/disk/mdraid_linux.c @@ -0,0 +1,244 @@ +/* mdraid_linux.c - module to handle linux softraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define GRUB_RAID_RESERVED_BYTES (64 * 1024) +#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) + +#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ + - GRUB_RAID_RESERVED_SECTORS) + +#define GRUB_RAID_SB_BYTES 4096 +#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) +#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define GRUB_RAID_SB_GENERIC_OFFSET 0 + +#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 +#define GRUB_RAID_SB_DISKS_OFFSET 128 +#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 + +#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 +#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 +#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ + + GRUB_RAID_SB_GENERIC_STATE_WORDS) +#define GRUB_RAID_SB_PERSONALITY_WORDS 64 +#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 +#define GRUB_RAID_SB_DISKS 27 +#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ + - GRUB_RAID_SB_PERSONALITY_WORDS \ + - GRUB_RAID_SB_DISKS_WORDS \ + - GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ + + GRUB_RAID_SB_PERSONALITY_WORDS \ + + GRUB_RAID_SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ +#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ +#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ +#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + +#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. + * read requests will only be sent here in + * dire need + */ + + +#define GRUB_RAID_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define GRUB_RAID_SB_CLEAN 0 +#define GRUB_RAID_SB_ERRORS 1 + +#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + +struct grub_raid_disk_09 { + grub_uint32_t number; /* 0 Device number in the entire set */ + grub_uint32_t major; /* 1 Device major number */ + grub_uint32_t minor; /* 2 Device minor number */ + grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ + grub_uint32_t state; /* 4 Operational state */ + grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 { + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* 0 MD identifier */ + grub_uint32_t major_version; /* 1 major version to which the set conforms */ + grub_uint32_t minor_version; /* 2 minor version ... */ + grub_uint32_t patch_version; /* 3 patchlevel version ... */ + grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ + grub_uint32_t set_uuid0; /* 5 Raid set identifier */ + grub_uint32_t ctime; /* 6 Creation time */ + grub_uint32_t level; /* 7 Raid personality */ + grub_uint32_t size; /* 8 Apparent size of each individual disk */ + grub_uint32_t nr_disks; /* 9 total disks in the raid set */ + grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ + grub_uint32_t md_minor; /* 11 preferred MD minor device number */ + grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ + grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ + grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ + grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ + grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* 0 Superblock update time */ + grub_uint32_t state; /* 1 State bits (clean, ...) */ + grub_uint32_t active_disks; /* 2 Number of currently active disks */ + grub_uint32_t working_disks; /* 3 Number of working disks */ + grub_uint32_t failed_disks; /* 4 Number of failed disks */ + grub_uint32_t spare_disks; /* 5 Number of spare disks */ + grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ +#ifdef GRUB_HOST_WORDS_BIGENDIAN + grub_uint32_t events_hi; /* 7 high-order of superblock update count */ + grub_uint32_t events_lo; /* 8 low-order of superblock update count */ + grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ + grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ +#else + grub_uint32_t events_lo; /* 7 low-order of superblock update count */ + grub_uint32_t events_hi; /* 8 high-order of superblock update count */ + grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ + grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ +#endif + grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ + grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* 0 the array's physical layout */ + grub_uint32_t chunk_size; /* 1 chunk size in bytes */ + grub_uint32_t root_pv; /* 2 LV root PV */ + grub_uint32_t root_block; /* 3 LV root block */ + grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +}; + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = GRUB_RAID_NEW_SIZE_SECTORS(size); + + if (grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != GRUB_RAID_SB_MAGIC) + return grub_error (GRUB_ERR_BAD_DEVICE, "not mdraid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + /* FIXME: Support all RAID levels. */ + if (sb.level != 0 && sb.level != 1 && sb.level != 5) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", + sb.level); + + /* FIXME: Support all layouts. */ + if (sb.level == 5 && sb.layout != 2) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID5 layout: %d", + sb.layout); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = 0; + array->total_devs = sb.nr_disks; + array->disk_size = (sb.size) ? sb.size * 2 : sector; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (! array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = +{ + .name = "mdraid", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} + +GRUB_MOD_FINI(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 731bf1f..521f1ac 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -112,9 +112,15 @@ grub_raid_open (const char *name, grub_disk_t disk) switch (array->level) { case 0: - /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; - break; + { + unsigned i; + + disk->total_sectors = 0; + for (i = 0; i < array->total_devs; i++) + disk->total_sectors += array->device[i]->total_sectors; + + break; + } case 1: disk->total_sectors = array->disk_size; @@ -152,56 +158,39 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, grub_uint32_t b; unsigned int disknr; grub_disk_addr_t read_sector; - grub_size_t read_size; /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, array->total_devs, &disknr); - - a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; - - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; + a = grub_divmod64 (sector, array->chunk_size, &b); + a = grub_divmod64 (a, array->total_devs, &disknr); + read_sector = a * array->chunk_size; while (1) { - grub_uint32_t i; + grub_size_t read_size; - err = grub_disk_read (array->device[disknr], read_sector, 0, + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + err = grub_disk_read (array->device[disknr], read_sector + b, 0, read_size << GRUB_DISK_SECTOR_BITS, buf); if (err) break; - buf += read_size; + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - + b = 0; disknr++; /* See whether the disk was the last disk, and start reading from the first disk in that case. */ if (disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - } + { + read_sector += array->chunk_size; + disknr = 0; + } } } break; @@ -348,169 +337,103 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *scanner_name) { - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); - - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } - - /* FIXME: Check the checksum. */ - - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } - - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } array->name = grub_malloc (13); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } grub_sprintf (array->name, "md%d", array->number); - grub_dprintf ("raid", "Found array: %s\n", array->name); + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, + scanner_name); /* Add our new array to the list. */ array->next = array_list; @@ -518,47 +441,115 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) + return 0; +} + + +static grub_raid_t grub_raid_list; + +static void +grub_raid_scan_device (int head_only) +{ + auto int hook (const char *name); + int hook (const char *name) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); + + disk = grub_disk_open (name); + if (!disk) + return 0; + + if ((disk->total_sectors == ULONG_MAX) || (disk->total_sectors == 0)) + { + grub_disk_close (disk); + return 0; + } + + for (p = grub_raid_list; p; p = p->next) + { + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + return 0; + + break; + } + + if (grub_errno != GRUB_ERR_BAD_DEVICE) + grub_print_error (); + + grub_errno = GRUB_ERR_NONE; + if (head_only) + break; + } + + grub_disk_close (disk); + + return 0; } - if (! array->device[sb.this_disk.number]) + grub_device_iterate (&hook); +} + +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + struct grub_raid_array *p; + int i; - return 0; + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); } - array->nr_devs++; + array_list = 0; +} - return 0; +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; + grub_raid_scan_device (1); +} + +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; + + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} + +void +grub_raid_rescan (void) +{ + free_array (); + grub_raid_scan_device (0); } static struct grub_disk_dev grub_raid_dev = @@ -579,12 +570,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..ca2edb7 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,43 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ int layout; /* Only for RAID 5. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) - -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) - -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ - -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ - - -#define GRUB_RAID_SB_MAGIC 0xa92b4efc - -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 +struct grub_raid +{ + const char *name; -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; + struct grub_raid *next; }; +typedef struct grub_raid *grub_raid_t; -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +void grub_raid_rescan (void); #endif /* ! GRUB_RAID_H */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..29234ac 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,6 +29,7 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> #include <grub_fstest_init.h> @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -273,32 +273,61 @@ cmd_hex (char *pathname) return 0; } - read_file (pathname, hex_hook); + if (pathname) + read_file (pathname, hex_hook); + else + { + char buf[BUF_SIZE]; + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device"); + + if (! leng) + leng = GRUB_DISK_SECTOR_SIZE; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d\n", + skip, len); + + hexdump (skip, buf, len); + + skip += len; + leng -= len; + } + + grub_device_close (dev); + } } static void -fstest (char *image_path, int cmd, int n, char **args) +fstest (char **images, int num_disks, int cmd, int n, char **args) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; - - - grub_sprintf (host_file, "(host)/%s", image_path); + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails.\n"); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) cmd_cmp (args[0], args[1]); break; case CMD_HEX: - cmd_hex (args[0]); + cmd_hex ((n == 0) ? 0 : args[0]); break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ ls PATH list files in PATH\n\ cp SRC DEST copy file to local system\n\ cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + hex [FILE] Hex dump FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,35 +481,29 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) - { - fprintf (stderr, "No path is specified.\n"); - usage (1); - } - - image_path = argv[optind]; - - if (*image_path != '/') + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "Must use absolute path.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; @@ -478,6 +521,7 @@ main (int argc, char *argv[]) else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +547,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-11 20:24 ` Bean @ 2008-08-11 20:30 ` Robert Millan 2008-08-12 11:49 ` Bean 0 siblings, 1 reply; 24+ messages in thread From: Robert Millan @ 2008-08-11 20:30 UTC (permalink / raw) To: The development of GRUB 2 On Tue, Aug 12, 2008 at 04:24:26AM +0800, Bean wrote: > - disk/lvm.c disk/raid.c grub_probe_init.c > + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c Hi, I haven't tried, but I think this module split would break grub-install & update-grub. Please check those out before committing! -- Robert Millan The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and how) you may access your data; but nobody's threatening your freedom: we still allow you to remove your data and not access it at all." ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-11 20:30 ` Robert Millan @ 2008-08-12 11:49 ` Bean 2008-08-12 12:24 ` Felix Zielcke ` (2 more replies) 0 siblings, 3 replies; 24+ messages in thread From: Bean @ 2008-08-12 11:49 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 2613 bytes --] On Tue, Aug 12, 2008 at 4:30 AM, Robert Millan <rmh@aybabtu.com> wrote: > On Tue, Aug 12, 2008 at 04:24:26AM +0800, Bean wrote: >> - disk/lvm.c disk/raid.c grub_probe_init.c >> + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c > > Hi, > > I haven't tried, but I think this module split would break grub-install & > update-grub. Please check those out before committing! Hi, Ok, I make some adjustment, now grub-install & update-grub works for raid device. If no one objects, I'd commit this soon. 2008-08-12 Bean <bean123ch@gmail.com> * conf/common.rmk (grub_probe_SOURCES): Add disk/mdraid_linux.c. (grub_fstest_SOURCES): Add disk/mdraid_linux.c and disk/dmraid_nvidia.c. (pkglib_MODULES): Add mdraid.mod and dm_nv.mod. (mdraid_mod_SOURCES): New macro. (mdraid_mod_CFLAGS): Likewise. (mdraid_mod_LDFLAGS): Likewise. (dm_nv_mod_SOURCES): Likewise. (dm_nv_mod_CFLAGS): Likewise. (dm_nv_mod_LDFLAGS): Likewise. * conf/i386-pc.rmk (grub_setup_SOURCES): Add disk/mdraid_linux.c. (grub_emu_SOURCES): Add disk/mdraid_linux.c and disk/dmraid_nvidia.c. * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add disk/mdraid_linux.c and disk/dmraid_nvidia.c. * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. * disk/mdraid_linux.c: New file. * disk/dmraid_nvidia.c: New file. * disk/i386/pc/biosdisk.c: Set total_sectors of cdrom device to ULONG_MAX. * disk/raid.c (grub_raid_open): Calculate total_sectors of raid0 by adding the size of individual disk (they can have different size). (grub_raid_read): Simply raid0 code, also fix a serious bug. (grub_raid_scan_device): Remove code specific to mdraid. (grub_raid_list): New variable. (free_array): New function. (grub_raid_register): Likewise. (grub_raid_unregister): Likewise. (grub_raid_rescan): Likewise. (GRUB_MOD_INIT): Don't iterate device here. (GRUB_MOD_FINI): Use free_array to release resource. * include/grub/raid.h: Remove macro and structure specific to mdraid. * util/grub-fstest.c: Add #include <grub/raid.h>. (part): Removed. (cmd_hex): Handle partition as well. (fstest): Handle multiple disks. (options): Remove part, raw and long, add root and diskcount. (usage): Change hex, remove -p, -r, -l, add -r and -c. (main): Find the first non option entry and ignore subsequence options, add handling for the new options, support multiple disks. * util/grub-probe.c (probe): Add mdraid to abstraction_name. -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_6.diff --] [-- Type: text/x-diff; name=raid_6.diff, Size: 49971 bytes --] diff --git a/conf/common.rmk b/conf/common.rmk index 95859f7..3876ce4 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -15,7 +15,7 @@ grub_probe_SOURCES = util/grub-probe.c \ \ partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c \ - disk/lvm.c disk/raid.c grub_probe_init.c + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c ifeq ($(enable_grub_fstest), yes) bin_UTILITIES += grub-fstest @@ -35,6 +35,7 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c # For the parser. @@ -264,7 +265,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod lvm.mod mdraid.mod dm_nv.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -276,6 +277,16 @@ lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk index 606b99c..74808ca 100644 --- a/conf/i386-coreboot.rmk +++ b/conf/i386-coreboot.rmk @@ -92,7 +92,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk index 2ce21b1..705e4ef 100644 --- a/conf/i386-efi.rmk +++ b/conf/i386-efi.rmk @@ -62,7 +62,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index a93845e..e996865 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -90,7 +90,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index c1e4ac4..f3142e4 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -102,7 +102,7 @@ grub_setup_SOURCES = util/i386/pc/grub-setup.c util/biosdisk.c \ \ partmap/pc.c partmap/gpt.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/lvm.c \ util/raid.c util/lvm.c \ grub_setup_init.c @@ -142,7 +142,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk index 0ed75f3..15e2fae 100644 --- a/conf/powerpc-ieee1275.rmk +++ b/conf/powerpc-ieee1275.rmk @@ -73,7 +73,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/powerpc/ieee1275/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_script.tab.c grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk index 4f8abba..8553226 100644 --- a/conf/x86_64-efi.rmk +++ b/conf/x86_64-efi.rmk @@ -64,7 +64,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c new file mode 100644 index 0000000..82eb26a --- /dev/null +++ b/disk/dmraid_nvidia.c @@ -0,0 +1,165 @@ +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +#define NV_SIGNATURES 4 + +#define NV_IDLE 0 +#define NV_SCDB_INIT_RAID 2 +#define NV_SCDB_REBUILD_RAID 3 +#define NV_SCDB_UPGRADE_RAID 4 +#define NV_SCDB_SYNC_RAID 5 + +#define NV_LEVEL_UNKNOWN 0x00 +#define NV_LEVEL_JBOD 0xFF +#define NV_LEVEL_0 0x80 +#define NV_LEVEL_1 0x81 +#define NV_LEVEL_3 0x83 +#define NV_LEVEL_5 0x85 +#define NV_LEVEL_10 0x8a +#define NV_LEVEL_1_0 0x8180 + +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ + +struct grub_raid_nv_array +{ + grub_uint32_t version; + grub_uint32_t signature[NV_SIGNATURES]; + grub_uint8_t raid_job_code; + grub_uint8_t stripe_width; + grub_uint8_t total_volumes; + grub_uint8_t original_width; + grub_uint32_t raid_level; + grub_uint32_t stripe_block_size; + grub_uint32_t stripe_block_size_bytes; + grub_uint32_t stripe_block_size_log2; + grub_uint32_t stripe_mask; + grub_uint32_t stripe_size; + grub_uint32_t stripe_size_bytes; + grub_uint32_t raid_job_mask; + grub_uint32_t original_capacity; + grub_uint32_t flags; +}; + +#define NV_ID_LENGTH 8 +#define NV_ID_STRING "NVIDIA" +#define NV_VERSION 100 + +#define NV_PRODUCTIDS 16 +#define NV_PRODUCTREVISIONS 4 + +struct grub_raid_nv_super +{ + grub_uint8_t vendor[NV_ID_LENGTH]; /* 0x00 - 0x07 ID string. */ + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ + grub_uint8_t reserved; /* 0x13. */ + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ + grub_uint8_t product_id[NV_PRODUCTIDS]; /* 0x1C - 0x2B Array product ID. */ + grub_uint8_t product_rev[NV_PRODUCTREVISIONS]; /* 0x2C - 0x2F Array product revision */ + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ + struct grub_raid_nv_array array; /* Array information */ +} __attribute__ ((packed)); + +static grub_err_t +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + struct grub_raid_nv_super sb; + grub_uint32_t *uuid; + + if (disk->partition) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "skip partition"); + + sector = grub_disk_get_size (disk) - 2; + + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) + return grub_errno; + + if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + if (sb.version != NV_VERSION) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unknown version: %d.%d", sb.version); + + switch (sb.array.raid_level) + { + case NV_LEVEL_0: + array->level = 0; + array->disk_size = sb.capacity / sb.array.total_volumes; + break; + + case NV_LEVEL_1: + array->level = 1; + array->disk_size = sb.capacity; + break; + + case NV_LEVEL_5: + array->level = 5; + array->disk_size = sb.capacity / (sb.array.total_volumes - 1); + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.array.raid_level); + } + + array->number = 0; + array->total_devs = sb.array.total_volumes; + array->chunk_size = sb.array.stripe_block_size; + array->index = sb.unit_number; + array->uuid_len = sizeof (sb.array.signature); + array->uuid = grub_malloc (sizeof (sb.array.signature)); + if (! array->uuid) + return grub_errno; + + grub_memcpy (array->uuid, (char *) &sb.array.signature, + sizeof (sb.array.signature)); + + return 0; +} + +static struct grub_raid grub_dmraid_nv_dev = +{ + .name = "dmraid_nv", + .detect = grub_dmraid_nv_detect, + .next = 0 +}; + +GRUB_MOD_INIT(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} + +GRUB_MOD_FINI(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..8b7f5ed 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; - total_sectors = 9000000; /* TODO: get the correct size. */ + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c new file mode 100644 index 0000000..c77f2e2 --- /dev/null +++ b/disk/mdraid_linux.c @@ -0,0 +1,243 @@ +/* mdraid_linux.c - module to handle linux softraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define GRUB_RAID_RESERVED_BYTES (64 * 1024) +#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) + +#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ + - GRUB_RAID_RESERVED_SECTORS) + +#define GRUB_RAID_SB_BYTES 4096 +#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) +#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define GRUB_RAID_SB_GENERIC_OFFSET 0 + +#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 +#define GRUB_RAID_SB_DISKS_OFFSET 128 +#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 + +#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 +#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 +#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ + + GRUB_RAID_SB_GENERIC_STATE_WORDS) +#define GRUB_RAID_SB_PERSONALITY_WORDS 64 +#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 +#define GRUB_RAID_SB_DISKS 27 +#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ + - GRUB_RAID_SB_PERSONALITY_WORDS \ + - GRUB_RAID_SB_DISKS_WORDS \ + - GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ + + GRUB_RAID_SB_PERSONALITY_WORDS \ + + GRUB_RAID_SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ +#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ +#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ +#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + +#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. + * read requests will only be sent here in + * dire need + */ + + +#define GRUB_RAID_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define GRUB_RAID_SB_CLEAN 0 +#define GRUB_RAID_SB_ERRORS 1 + +#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + +struct grub_raid_disk_09 { + grub_uint32_t number; /* 0 Device number in the entire set */ + grub_uint32_t major; /* 1 Device major number */ + grub_uint32_t minor; /* 2 Device minor number */ + grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ + grub_uint32_t state; /* 4 Operational state */ + grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 { + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* 0 MD identifier */ + grub_uint32_t major_version; /* 1 major version to which the set conforms */ + grub_uint32_t minor_version; /* 2 minor version ... */ + grub_uint32_t patch_version; /* 3 patchlevel version ... */ + grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ + grub_uint32_t set_uuid0; /* 5 Raid set identifier */ + grub_uint32_t ctime; /* 6 Creation time */ + grub_uint32_t level; /* 7 Raid personality */ + grub_uint32_t size; /* 8 Apparent size of each individual disk */ + grub_uint32_t nr_disks; /* 9 total disks in the raid set */ + grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ + grub_uint32_t md_minor; /* 11 preferred MD minor device number */ + grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ + grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ + grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ + grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ + grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* 0 Superblock update time */ + grub_uint32_t state; /* 1 State bits (clean, ...) */ + grub_uint32_t active_disks; /* 2 Number of currently active disks */ + grub_uint32_t working_disks; /* 3 Number of working disks */ + grub_uint32_t failed_disks; /* 4 Number of failed disks */ + grub_uint32_t spare_disks; /* 5 Number of spare disks */ + grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ +#ifdef GRUB_HOST_WORDS_BIGENDIAN + grub_uint32_t events_hi; /* 7 high-order of superblock update count */ + grub_uint32_t events_lo; /* 8 low-order of superblock update count */ + grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ + grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ +#else + grub_uint32_t events_lo; /* 7 low-order of superblock update count */ + grub_uint32_t events_hi; /* 8 high-order of superblock update count */ + grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ + grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ +#endif + grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ + grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* 0 the array's physical layout */ + grub_uint32_t chunk_size; /* 1 chunk size in bytes */ + grub_uint32_t root_pv; /* 2 LV root PV */ + grub_uint32_t root_block; /* 3 LV root block */ + grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +}; + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = GRUB_RAID_NEW_SIZE_SECTORS(size); + + if (grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != GRUB_RAID_SB_MAGIC) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + /* FIXME: Support all RAID levels. */ + if (sb.level != 0 && sb.level != 1 && sb.level != 5) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", + sb.level); + + /* FIXME: Support all layouts. */ + if (sb.level == 5 && sb.layout != 2) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID5 layout: %d", + sb.layout); + + array->number = sb.md_minor; + array->level = sb.level; + array->total_devs = sb.nr_disks; + array->disk_size = (sb.size) ? sb.size * 2 : sector; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (! array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = +{ + .name = "mdraid", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} + +GRUB_MOD_FINI(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 731bf1f..3333f4b 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -112,9 +112,15 @@ grub_raid_open (const char *name, grub_disk_t disk) switch (array->level) { case 0: - /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; - break; + { + unsigned i; + + disk->total_sectors = 0; + for (i = 0; i < array->total_devs; i++) + disk->total_sectors += array->device[i]->total_sectors; + + break; + } case 1: disk->total_sectors = array->disk_size; @@ -152,56 +158,39 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, grub_uint32_t b; unsigned int disknr; grub_disk_addr_t read_sector; - grub_size_t read_size; /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, array->total_devs, &disknr); - - a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; - - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; + a = grub_divmod64 (sector, array->chunk_size, &b); + a = grub_divmod64 (a, array->total_devs, &disknr); + read_sector = a * array->chunk_size; while (1) { - grub_uint32_t i; + grub_size_t read_size; - err = grub_disk_read (array->device[disknr], read_sector, 0, + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + err = grub_disk_read (array->device[disknr], read_sector + b, 0, read_size << GRUB_DISK_SECTOR_BITS, buf); if (err) break; - buf += read_size; + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - + b = 0; disknr++; /* See whether the disk was the last disk, and start reading from the first disk in that case. */ if (disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - } + { + read_sector += array->chunk_size; + disknr = 0; + } } } break; @@ -348,169 +337,103 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *scanner_name) { - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); - - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } - - /* FIXME: Check the checksum. */ - - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } - - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } array->name = grub_malloc (13); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } grub_sprintf (array->name, "md%d", array->number); - grub_dprintf ("raid", "Found array: %s\n", array->name); + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, + scanner_name); /* Add our new array to the list. */ array->next = array_list; @@ -518,47 +441,117 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) + return 0; +} + + +static grub_raid_t grub_raid_list; + +static void +grub_raid_scan_device (int head_only) +{ + auto int hook (const char *name); + int hook (const char *name) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); + + disk = grub_disk_open (name); + if (!disk) + return 0; + + if (disk->total_sectors == ULONG_MAX) + { + grub_disk_close (disk); + return 0; + } + + for (p = grub_raid_list; p; p = p->next) + { + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + return 0; + + break; + } + + /* This error usually means it's not raid, no need to display + it. */ + if (grub_errno != GRUB_ERR_OUT_OF_RANGE) + grub_print_error (); + + grub_errno = GRUB_ERR_NONE; + if (head_only) + break; + } + + grub_disk_close (disk); + + return 0; } - if (! array->device[sb.this_disk.number]) + grub_device_iterate (&hook); +} + +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + struct grub_raid_array *p; + int i; - return 0; + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); } - array->nr_devs++; + array_list = 0; +} - return 0; +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; + grub_raid_scan_device (1); +} + +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; + + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} + +void +grub_raid_rescan (void) +{ + free_array (); + grub_raid_scan_device (0); } static struct grub_disk_dev grub_raid_dev = @@ -579,12 +572,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..0920f0f 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,42 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ - int layout; /* Only for RAID 5. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) - -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) - -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ - -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ - - -#define GRUB_RAID_SB_MAGIC 0xa92b4efc - -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 +struct grub_raid +{ + const char *name; -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; + struct grub_raid *next; }; +typedef struct grub_raid *grub_raid_t; -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +void grub_raid_rescan (void); #endif /* ! GRUB_RAID_H */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..29234ac 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,6 +29,7 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> #include <grub_fstest_init.h> @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -273,32 +273,61 @@ cmd_hex (char *pathname) return 0; } - read_file (pathname, hex_hook); + if (pathname) + read_file (pathname, hex_hook); + else + { + char buf[BUF_SIZE]; + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device"); + + if (! leng) + leng = GRUB_DISK_SECTOR_SIZE; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d\n", + skip, len); + + hexdump (skip, buf, len); + + skip += len; + leng -= len; + } + + grub_device_close (dev); + } } static void -fstest (char *image_path, int cmd, int n, char **args) +fstest (char **images, int num_disks, int cmd, int n, char **args) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; - - - grub_sprintf (host_file, "(host)/%s", image_path); + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails.\n"); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) cmd_cmp (args[0], args[1]); break; case CMD_HEX: - cmd_hex (args[0]); + cmd_hex ((n == 0) ? 0 : args[0]); break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ ls PATH list files in PATH\n\ cp SRC DEST copy file to local system\n\ cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + hex [FILE] Hex dump FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,35 +481,29 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) - { - fprintf (stderr, "No path is specified.\n"); - usage (1); - } - - image_path = argv[optind]; - - if (*image_path != '/') + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "Must use absolute path.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; @@ -478,6 +521,7 @@ main (int argc, char *argv[]) else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +547,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); diff --git a/util/grub-probe.c b/util/grub-probe.c index a4f51e2..ce9cbff 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -142,7 +142,7 @@ probe (const char *path, char *device_name) abstraction_name = "lvm"; break; case GRUB_DEV_ABSTRACTION_RAID: - abstraction_name = "raid"; + abstraction_name = "raid mdraid"; break; default: grub_util_info ("did not find LVM/RAID in %s, assuming raw device", device_name); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-12 11:49 ` Bean @ 2008-08-12 12:24 ` Felix Zielcke 2008-08-12 13:32 ` Robert Millan 2008-08-13 10:05 ` Marco Gerards 2 siblings, 0 replies; 24+ messages in thread From: Felix Zielcke @ 2008-08-12 12:24 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 616 bytes --] Am Dienstag, den 12.08.2008, 19:49 +0800 schrieb Bean: > Hi, > > Ok, I make some adjustment, now grub-install & update-grub works for > raid device. > > If no one objects, I'd commit this soon. > Before your mail came in, I just talked with Robert about the fixes which I'd like to have in lenny. 13:54:48 < nyu> fzielcke: okay then. if you see bean could you ask him to commit this fix separately? He wants to have your little one line raid 0 fix commited separately I attached the .diff for this against current SVN so you don't need to make one. This patch will be in the next upload targeted for lenny. [-- Attachment #2: raid0_fix.diff --] [-- Type: text/x-patch, Size: 335 bytes --] Index: disk/raid.c =================================================================== --- disk/raid.c (Revision 1800) +++ disk/raid.c (Arbeitskopie) @@ -177,7 +177,7 @@ if (err) break; - buf += read_size; + buf += (read_size << GRUB_DISK_SECTOR_BITS); size -= read_size; if (! size) break; ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-12 11:49 ` Bean 2008-08-12 12:24 ` Felix Zielcke @ 2008-08-12 13:32 ` Robert Millan 2008-08-13 10:05 ` Marco Gerards 2 siblings, 0 replies; 24+ messages in thread From: Robert Millan @ 2008-08-12 13:32 UTC (permalink / raw) To: The development of GRUB 2 On Tue, Aug 12, 2008 at 07:49:19PM +0800, Bean wrote: > > Ok, I make some adjustment, now grub-install & update-grub works for > raid device. > [...] > --- a/util/grub-probe.c > +++ b/util/grub-probe.c > @@ -142,7 +142,7 @@ probe (const char *path, char *device_name) > abstraction_name = "lvm"; > break; > case GRUB_DEV_ABSTRACTION_RAID: > - abstraction_name = "raid"; > + abstraction_name = "raid mdraid"; Should be fine, I think. > - buf += read_size; > + buf += read_size << GRUB_DISK_SECTOR_BITS; Felix told me this fixes a problem in current RAID code; when you check your code in, please consider including this bugfix in a separate commit. This makes it easier to track things. Thanks! -- Robert Millan The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and how) you may access your data; but nobody's threatening your freedom: we still allow you to remove your data and not access it at all." ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-12 11:49 ` Bean 2008-08-12 12:24 ` Felix Zielcke 2008-08-12 13:32 ` Robert Millan @ 2008-08-13 10:05 ` Marco Gerards 2008-08-13 18:43 ` Bean 2 siblings, 1 reply; 24+ messages in thread From: Marco Gerards @ 2008-08-13 10:05 UTC (permalink / raw) To: The development of GRUB 2 Bean <bean123ch@gmail.com> writes: > On Tue, Aug 12, 2008 at 4:30 AM, Robert Millan <rmh@aybabtu.com> wrote: >> On Tue, Aug 12, 2008 at 04:24:26AM +0800, Bean wrote: >>> - disk/lvm.c disk/raid.c grub_probe_init.c >>> + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c >> >> Hi, >> >> I haven't tried, but I think this module split would break grub-install & >> update-grub. Please check those out before committing! > > Hi, > > Ok, I make some adjustment, now grub-install & update-grub works for > raid device. Great > If no one objects, I'd commit this soon. > > 2008-08-12 Bean <bean123ch@gmail.com> > > * conf/common.rmk (grub_probe_SOURCES): Add disk/mdraid_linux.c. > (grub_fstest_SOURCES): Add disk/mdraid_linux.c and disk/dmraid_nvidia.c. > (pkglib_MODULES): Add mdraid.mod and dm_nv.mod. > (mdraid_mod_SOURCES): New macro. > (mdraid_mod_CFLAGS): Likewise. > (mdraid_mod_LDFLAGS): Likewise. > (dm_nv_mod_SOURCES): Likewise. > (dm_nv_mod_CFLAGS): Likewise. > (dm_nv_mod_LDFLAGS): Likewise. > > * conf/i386-pc.rmk (grub_setup_SOURCES): Add disk/mdraid_linux.c. > (grub_emu_SOURCES): Add disk/mdraid_linux.c and disk/dmraid_nvidia.c. > > * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add disk/mdraid_linux.c > and disk/dmraid_nvidia.c. > > * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. > > * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. > > * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. > > * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. > > * disk/mdraid_linux.c: New file. > > * disk/dmraid_nvidia.c: New file. > > * disk/i386/pc/biosdisk.c: Set total_sectors of cdrom device to > ULONG_MAX. For which function? > * disk/raid.c (grub_raid_open): Calculate total_sectors of raid0 > by adding the size of individual disk (they can have different size). > (grub_raid_read): Simply raid0 code, also fix a serious bug. > (grub_raid_scan_device): Remove code specific to mdraid. > (grub_raid_list): New variable. > (free_array): New function. > (grub_raid_register): Likewise. > (grub_raid_unregister): Likewise. > (grub_raid_rescan): Likewise. > (GRUB_MOD_INIT): Don't iterate device here. > (GRUB_MOD_FINI): Use free_array to release resource. > > * include/grub/raid.h: Remove macro and structure specific to mdraid. > > * util/grub-fstest.c: Add #include <grub/raid.h>. > (part): Removed. > (cmd_hex): Handle partition as well. > (fstest): Handle multiple disks. > (options): Remove part, raw and long, add root and diskcount. > (usage): Change hex, remove -p, -r, -l, add -r and -c. > (main): Find the first non option entry and ignore subsequence options, > add handling for the new options, support multiple disks. > > * util/grub-probe.c (probe): Add mdraid to abstraction_name. > > -- > Bean > > diff --git a/conf/common.rmk b/conf/common.rmk > index 95859f7..3876ce4 100644 > --- a/conf/common.rmk > +++ b/conf/common.rmk > @@ -15,7 +15,7 @@ grub_probe_SOURCES = util/grub-probe.c \ > \ > partmap/pc.c partmap/apple.c partmap/gpt.c \ > kern/fs.c kern/env.c fs/fshelp.c \ > - disk/lvm.c disk/raid.c grub_probe_init.c > + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c > > ifeq ($(enable_grub_fstest), yes) > bin_UTILITIES += grub-fstest > @@ -35,6 +35,7 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ > \ > kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ > kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ > + disk/mdraid_linux.c disk/dmraid_nvidia.c \ > grub_fstest_init.c > > # For the parser. > @@ -264,7 +265,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) > > # Special disk structures > > -pkglib_MODULES += raid.mod lvm.mod > +pkglib_MODULES += raid.mod lvm.mod mdraid.mod dm_nv.mod > > # For raid.mod > raid_mod_SOURCES = disk/raid.c > @@ -276,6 +277,16 @@ lvm_mod_SOURCES = disk/lvm.c > lvm_mod_CFLAGS = $(COMMON_CFLAGS) > lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) > > +# For mdraid.mod > +mdraid_mod_SOURCES = disk/mdraid_linux.c > +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) > +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) > + > +# For dm_nv.mod > +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c > +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) > +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) > + > # Commands. > pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ > cmp.mod cat.mod help.mod font.mod search.mod \ > diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk > index 606b99c..74808ca 100644 > --- a/conf/i386-coreboot.rmk > +++ b/conf/i386-coreboot.rmk > @@ -92,7 +92,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ > util/biosdisk.c util/getroot.c \ > util/i386/pc/misc.c \ > \ > - disk/raid.c disk/lvm.c \ > + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ > grub_emu_init.c > > grub_emu_LDFLAGS = $(LIBCURSES) > diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk > index 2ce21b1..705e4ef 100644 > --- a/conf/i386-efi.rmk > +++ b/conf/i386-efi.rmk > @@ -62,7 +62,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ > util/biosdisk.c util/getroot.c \ > util/i386/pc/misc.c \ > \ > - disk/raid.c disk/lvm.c \ > + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ > grub_emu_init.c > > grub_emu_LDFLAGS = $(LIBCURSES) > diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk > index a93845e..e996865 100644 > --- a/conf/i386-ieee1275.rmk > +++ b/conf/i386-ieee1275.rmk > @@ -90,7 +90,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ > util/biosdisk.c util/getroot.c \ > util/i386/pc/misc.c \ > \ > - disk/raid.c disk/lvm.c \ > + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ > grub_emu_init.c > > grub_emu_LDFLAGS = $(LIBCURSES) > diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk > index c1e4ac4..f3142e4 100644 > --- a/conf/i386-pc.rmk > +++ b/conf/i386-pc.rmk > @@ -102,7 +102,7 @@ grub_setup_SOURCES = util/i386/pc/grub-setup.c util/biosdisk.c \ > \ > partmap/pc.c partmap/gpt.c \ > \ > - disk/raid.c disk/lvm.c \ > + disk/raid.c disk/mdraid_linux.c disk/lvm.c \ > util/raid.c util/lvm.c \ > grub_setup_init.c > > @@ -142,7 +142,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ > util/biosdisk.c util/getroot.c \ > util/i386/pc/misc.c \ > \ > - disk/raid.c disk/lvm.c \ > + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ > grub_emu_init.c > > grub_emu_LDFLAGS = $(LIBCURSES) > diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk > index 0ed75f3..15e2fae 100644 > --- a/conf/powerpc-ieee1275.rmk > +++ b/conf/powerpc-ieee1275.rmk > @@ -73,7 +73,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ > util/biosdisk.c util/getroot.c \ > util/powerpc/ieee1275/misc.c \ > \ > - disk/raid.c disk/lvm.c \ > + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ > grub_script.tab.c grub_emu_init.c > > grub_emu_LDFLAGS = $(LIBCURSES) > diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk > index 4f8abba..8553226 100644 > --- a/conf/x86_64-efi.rmk > +++ b/conf/x86_64-efi.rmk > @@ -64,7 +64,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ > util/biosdisk.c util/getroot.c \ > util/i386/pc/misc.c \ > \ > - disk/raid.c disk/lvm.c \ > + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ > grub_emu_init.c > > grub_emu_LDFLAGS = $(LIBCURSES) > diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c > new file mode 100644 > index 0000000..82eb26a > --- /dev/null > +++ b/disk/dmraid_nvidia.c > @@ -0,0 +1,165 @@ > +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ > +/* > + * GRUB -- GRand Unified Bootloader > + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <grub/dl.h> > +#include <grub/disk.h> > +#include <grub/mm.h> > +#include <grub/err.h> > +#include <grub/misc.h> > +#include <grub/raid.h> > + > +#define NV_SIGNATURES 4 > + > +#define NV_IDLE 0 > +#define NV_SCDB_INIT_RAID 2 > +#define NV_SCDB_REBUILD_RAID 3 > +#define NV_SCDB_UPGRADE_RAID 4 > +#define NV_SCDB_SYNC_RAID 5 > + > +#define NV_LEVEL_UNKNOWN 0x00 > +#define NV_LEVEL_JBOD 0xFF > +#define NV_LEVEL_0 0x80 > +#define NV_LEVEL_1 0x81 > +#define NV_LEVEL_3 0x83 > +#define NV_LEVEL_5 0x85 > +#define NV_LEVEL_10 0x8a > +#define NV_LEVEL_1_0 0x8180 > + > +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ > +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ > +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ > + > +struct grub_raid_nv_array > +{ > + grub_uint32_t version; > + grub_uint32_t signature[NV_SIGNATURES]; > + grub_uint8_t raid_job_code; > + grub_uint8_t stripe_width; > + grub_uint8_t total_volumes; > + grub_uint8_t original_width; > + grub_uint32_t raid_level; > + grub_uint32_t stripe_block_size; > + grub_uint32_t stripe_block_size_bytes; > + grub_uint32_t stripe_block_size_log2; > + grub_uint32_t stripe_mask; > + grub_uint32_t stripe_size; > + grub_uint32_t stripe_size_bytes; > + grub_uint32_t raid_job_mask; > + grub_uint32_t original_capacity; > + grub_uint32_t flags; > +}; > + > +#define NV_ID_LENGTH 8 > +#define NV_ID_STRING "NVIDIA" > +#define NV_VERSION 100 > + > +#define NV_PRODUCTIDS 16 > +#define NV_PRODUCTREVISIONS 4 > + > +struct grub_raid_nv_super > +{ > + grub_uint8_t vendor[NV_ID_LENGTH]; /* 0x00 - 0x07 ID string. */ > + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ > + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ > + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ > + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ > + grub_uint8_t reserved; /* 0x13. */ > + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ > + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ > + grub_uint8_t product_id[NV_PRODUCTIDS]; /* 0x1C - 0x2B Array product ID. */ > + grub_uint8_t product_rev[NV_PRODUCTREVISIONS]; /* 0x2C - 0x2F Array product revision */ > + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ > + struct grub_raid_nv_array array; /* Array information */ > +} __attribute__ ((packed)); These lines get very long (too long?). In general I prefer comments on the line before the code. > +static grub_err_t > +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) > +{ > + grub_disk_addr_t sector; > + struct grub_raid_nv_super sb; > + grub_uint32_t *uuid; > + > + if (disk->partition) > + return grub_error (GRUB_ERR_OUT_OF_RANGE, "skip partition"); > + > + sector = grub_disk_get_size (disk) - 2; > + > + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) > + return grub_errno; > + > + if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) > + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); > + > + if (sb.version != NV_VERSION) > + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, > + "Unknown version: %d.%d", sb.version); > + > + switch (sb.array.raid_level) > + { > + case NV_LEVEL_0: > + array->level = 0; > + array->disk_size = sb.capacity / sb.array.total_volumes; > + break; > + > + case NV_LEVEL_1: > + array->level = 1; > + array->disk_size = sb.capacity; > + break; > + > + case NV_LEVEL_5: > + array->level = 5; > + array->disk_size = sb.capacity / (sb.array.total_volumes - 1); > + break; > + > + default: > + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, > + "Unsupported RAID level: %d", sb.array.raid_level); > + } > + > + array->number = 0; > + array->total_devs = sb.array.total_volumes; > + array->chunk_size = sb.array.stripe_block_size; > + array->index = sb.unit_number; > + array->uuid_len = sizeof (sb.array.signature); > + array->uuid = grub_malloc (sizeof (sb.array.signature)); > + if (! array->uuid) > + return grub_errno; > + > + grub_memcpy (array->uuid, (char *) &sb.array.signature, > + sizeof (sb.array.signature)); > + > + return 0; > +} > + > +static struct grub_raid grub_dmraid_nv_dev = > +{ > + .name = "dmraid_nv", > + .detect = grub_dmraid_nv_detect, > + .next = 0 > +}; > + > +GRUB_MOD_INIT(dm_nv) > +{ > + grub_raid_register (&grub_dmraid_nv_dev); > +} > + > +GRUB_MOD_FINI(dm_nv) > +{ > + grub_raid_register (&grub_dmraid_nv_dev); > +} > diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c > index c8fd142..8b7f5ed 100644 > --- a/disk/i386/pc/biosdisk.c > +++ b/disk/i386/pc/biosdisk.c > @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) > { > data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; > data->sectors = 32; > - total_sectors = 9000000; /* TODO: get the correct size. */ > + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ You are cheating here! :-) > else if (drive & 0x80) > { > diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c > new file mode 100644 > index 0000000..c77f2e2 > --- /dev/null > +++ b/disk/mdraid_linux.c > @@ -0,0 +1,243 @@ > +/* mdraid_linux.c - module to handle linux softraid. */ > +/* > + * GRUB -- GRand Unified Bootloader > + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. > + */ > + > +#include <grub/dl.h> > +#include <grub/disk.h> > +#include <grub/mm.h> > +#include <grub/err.h> > +#include <grub/misc.h> > +#include <grub/raid.h> > + > +/* Linux RAID on disk structures and constants, > + copied from include/linux/raid/md_p.h. */ > + > +#define GRUB_RAID_RESERVED_BYTES (64 * 1024) > +#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) > + > +#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ > + - GRUB_RAID_RESERVED_SECTORS) > + > +#define GRUB_RAID_SB_BYTES 4096 > +#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) > +#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) > + > +/* > + * The following are counted in 32-bit words > + */ > +#define GRUB_RAID_SB_GENERIC_OFFSET 0 > + > +#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 > +#define GRUB_RAID_SB_DISKS_OFFSET 128 > +#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 > + > +#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 > +#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 > +#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ > + + GRUB_RAID_SB_GENERIC_STATE_WORDS) > +#define GRUB_RAID_SB_PERSONALITY_WORDS 64 > +#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 > +#define GRUB_RAID_SB_DISKS 27 > +#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) > +#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ > + - GRUB_RAID_SB_PERSONALITY_WORDS \ > + - GRUB_RAID_SB_DISKS_WORDS \ > + - GRUB_RAID_SB_DESCRIPTOR_WORDS) > +#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ > + + GRUB_RAID_SB_PERSONALITY_WORDS \ > + + GRUB_RAID_SB_DISKS_WORDS) The lines above are way too long > +/* > + * Device "operational" state bits > + */ > +#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ > +#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ > +#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ > +#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ > + > +#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. > + * read requests will only be sent here in > + * dire need > + */ Can you correctly format comments? Placing the comments before the #define makes the lines shorter. Perhaps they are too long (I didn't check) The same applies for code below > + > +#define GRUB_RAID_SB_MAGIC 0xa92b4efc > + > +/* > + * Superblock state bits > + */ > +#define GRUB_RAID_SB_CLEAN 0 > +#define GRUB_RAID_SB_ERRORS 1 > + > +#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ > + > +struct grub_raid_disk_09 { > + grub_uint32_t number; /* 0 Device number in the entire set */ > + grub_uint32_t major; /* 1 Device major number */ > + grub_uint32_t minor; /* 2 Device minor number */ > + grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ > + grub_uint32_t state; /* 4 Operational state */ > + grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; > +}; > + > +struct grub_raid_super_09 { > + /* > + * Constant generic information > + */ > + grub_uint32_t md_magic; /* 0 MD identifier */ > + grub_uint32_t major_version; /* 1 major version to which the set conforms */ > + grub_uint32_t minor_version; /* 2 minor version ... */ > + grub_uint32_t patch_version; /* 3 patchlevel version ... */ > + grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ > + grub_uint32_t set_uuid0; /* 5 Raid set identifier */ > + grub_uint32_t ctime; /* 6 Creation time */ > + grub_uint32_t level; /* 7 Raid personality */ > + grub_uint32_t size; /* 8 Apparent size of each individual disk */ > + grub_uint32_t nr_disks; /* 9 total disks in the raid set */ > + grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ > + grub_uint32_t md_minor; /* 11 preferred MD minor device number */ > + grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ > + grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ > + grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ > + grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ > + grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; > + > + /* > + * Generic state information > + */ > + grub_uint32_t utime; /* 0 Superblock update time */ > + grub_uint32_t state; /* 1 State bits (clean, ...) */ > + grub_uint32_t active_disks; /* 2 Number of currently active disks */ > + grub_uint32_t working_disks; /* 3 Number of working disks */ > + grub_uint32_t failed_disks; /* 4 Number of failed disks */ > + grub_uint32_t spare_disks; /* 5 Number of spare disks */ > + grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ > +#ifdef GRUB_HOST_WORDS_BIGENDIAN > + grub_uint32_t events_hi; /* 7 high-order of superblock update count */ > + grub_uint32_t events_lo; /* 8 low-order of superblock update count */ > + grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ > + grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ > +#else > + grub_uint32_t events_lo; /* 7 low-order of superblock update count */ > + grub_uint32_t events_hi; /* 8 high-order of superblock update count */ > + grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ > + grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ > +#endif Why? We have macros for this :-) > + grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ > + grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; > + > + /* > + * Personality information > + */ > + grub_uint32_t layout; /* 0 the array's physical layout */ > + grub_uint32_t chunk_size; /* 1 chunk size in bytes */ > + grub_uint32_t root_pv; /* 2 LV root PV */ > + grub_uint32_t root_block; /* 3 LV root block */ > + grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; > + > + /* > + * Disks information > + */ /* Disks information. */ > + struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; > + > + /* > + * Reserved > + */ /* Reserved. */ (I do not like this comment style) > + grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; > + > + /* > + * Active descriptor > + */ Same here. > + struct grub_raid_disk_09 this_disk; > +}; > + > +static grub_err_t > +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) > +{ > + grub_disk_addr_t sector; > + grub_uint64_t size; > + struct grub_raid_super_09 sb; > + grub_uint32_t *uuid; > + > + /* The sector where the RAID superblock is stored, if available. */ > + size = grub_disk_get_size (disk); > + sector = GRUB_RAID_NEW_SIZE_SECTORS(size); > + > + if (grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb)) > + return grub_errno; > + > + /* Look whether there is a RAID superblock. */ > + if (sb.md_magic != GRUB_RAID_SB_MAGIC) > + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); It looks like the comment is misaligned. > + /* FIXME: Also support version 1.0. */ > + if (sb.major_version != 0 || sb.minor_version != 90) > + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, > + "Unsupported RAID version: %d.%d", > + sb.major_version, sb.minor_version); > + > + /* FIXME: Check the checksum. */ > + > + /* FIXME: Support all RAID levels. */ > + if (sb.level != 0 && sb.level != 1 && sb.level != 5) > + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, > + "Unsupported RAID level: %d", > + sb.level); > + > + /* FIXME: Support all layouts. */ > + if (sb.level == 5 && sb.layout != 2) > + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, > + "Unsupported RAID5 layout: %d", > + sb.layout); > + > + array->number = sb.md_minor; > + array->level = sb.level; > + array->total_devs = sb.nr_disks; > + array->disk_size = (sb.size) ? sb.size * 2 : sector; > + array->chunk_size = sb.chunk_size >> 9; > + array->index = sb.this_disk.number; > + array->uuid_len = 16; > + array->uuid = grub_malloc (16); > + if (! array->uuid) > + return grub_errno; > + > + uuid = (grub_uint32_t *) array->uuid; > + uuid[0] = sb.set_uuid0; > + uuid[1] = sb.set_uuid1; > + uuid[2] = sb.set_uuid2; > + uuid[3] = sb.set_uuid3; > + > + return 0; > +} > + > +static struct grub_raid grub_mdraid_dev = > +{ > + .name = "mdraid", > + .detect = grub_mdraid_detect, > + .next = 0 > +}; > + > +GRUB_MOD_INIT(mdraid) > +{ > + grub_raid_register (&grub_mdraid_dev); > +} > + > +GRUB_MOD_FINI(mdraid) > +{ > + grub_raid_register (&grub_mdraid_dev); > +} > diff --git a/disk/raid.c b/disk/raid.c > index 731bf1f..3333f4b 100644 > --- a/disk/raid.c > +++ b/disk/raid.c > @@ -112,9 +112,15 @@ grub_raid_open (const char *name, grub_disk_t disk) > switch (array->level) > { > case 0: > - /* FIXME: RAID0 disks can have different sizes! */ > - disk->total_sectors = array->total_devs * array->disk_size; > - break; > + { > + unsigned i; > + > + disk->total_sectors = 0; > + for (i = 0; i < array->total_devs; i++) > + disk->total_sectors += array->device[i]->total_sectors; > + > + break; > + } > > case 1: > disk->total_sectors = array->disk_size; > @@ -152,56 +158,39 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, > grub_uint32_t b; > unsigned int disknr; > grub_disk_addr_t read_sector; > - grub_size_t read_size; > > /* Find the first sector to read. */ > - a = grub_divmod64 (sector, array->chunk_size, NULL); > - grub_divmod64 (a, array->total_devs, &disknr); > - > - a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL); > - grub_divmod64 (sector, array->chunk_size, &b); > - read_sector = a * array->chunk_size + b; > - > - grub_divmod64 (read_sector, array->chunk_size, &b); > - read_size = array->chunk_size - b; > - > - if (read_size > size) > - read_size = size; > + a = grub_divmod64 (sector, array->chunk_size, &b); > + a = grub_divmod64 (a, array->total_devs, &disknr); > + read_sector = a * array->chunk_size; > > while (1) > { > - grub_uint32_t i; > + grub_size_t read_size; > > - err = grub_disk_read (array->device[disknr], read_sector, 0, > + read_size = array->chunk_size - b; > + if (read_size > size) > + read_size = size; > + > + err = grub_disk_read (array->device[disknr], read_sector + b, 0, > read_size << GRUB_DISK_SECTOR_BITS, buf); > if (err) > break; > > - buf += read_size; > + buf += read_size << GRUB_DISK_SECTOR_BITS; > size -= read_size; > if (! size) > break; > > - if (size > array->chunk_size) > - read_size = array->chunk_size; > - else > - read_size = size; > - > - /* Check whether the sector was aligned on a chunk size > - boundary. If this isn't the case, it's the first read > - and the next read should be set back to start of the > - boundary. */ > - grub_divmod64 (read_sector, array->chunk_size, &i); > - read_sector -= i; > - > + b = 0; > disknr++; > /* See whether the disk was the last disk, and start > reading from the first disk in that case. */ > if (disknr == array->total_devs) > - { > - disknr = 0; > - read_sector += array->chunk_size; > - } > + { > + read_sector += array->chunk_size; > + disknr = 0; > + } > } > } > break; > @@ -348,169 +337,103 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), > return GRUB_ERR_NOT_IMPLEMENTED_YET; > } > > -static int > -grub_raid_scan_device (const char *name) > +static grub_err_t > +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, > + const char *scanner_name) > { > - grub_err_t err; > - grub_disk_t disk; > - grub_disk_addr_t sector; > - grub_uint64_t size; > - struct grub_raid_super_09 sb; > - struct grub_raid_array *p, *array = NULL; > - > - grub_dprintf ("raid", "Scanning for RAID devices\n"); > - > - disk = grub_disk_open (name); > - if (!disk) > - return 0; > - > - /* The sector where the RAID superblock is stored, if available. */ > - size = grub_disk_get_size (disk); > - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); > - > - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); > - grub_disk_close (disk); > - if (err) > - { > - grub_errno = GRUB_ERR_NONE; > - return 0; > - } > - > - /* Look whether there is a RAID superblock. */ > - if (sb.md_magic != GRUB_RAID_SB_MAGIC) > - return 0; > - > - /* FIXME: Also support version 1.0. */ > - if (sb.major_version != 0 || sb.minor_version != 90) > - { > - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, > - "Unsupported RAID version: %d.%d", > - sb.major_version, sb.minor_version); > - return 0; > - } > - > - /* FIXME: Check the checksum. */ > - > - /* FIXME: Support all RAID levels. */ > - if (sb.level != 0 && sb.level != 1 && sb.level != 5) > - { > - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, > - "Unsupported RAID level: %d", > - sb.level); > - return 0; > - } > - > - /* FIXME: Support all layouts. */ > - if (sb.level == 5 && sb.layout != 2) > - { > - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, > - "Unsupported RAID5 layout: %d", > - sb.layout); > - return 0; > - } > + struct grub_raid_array *array = 0, *p; > > /* See whether the device is part of an array we have already seen a > device from. */ > for (p = array_list; p != NULL; p = p->next) > - { > - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 > - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) > - { > - array = p; > - break; > - } > - } > - > - /* Do some checks before adding the device to the array. */ > - if (array) > - { > - /* FIXME: Check whether the update time of the superblocks are > - the same. */ > - > - if (array->total_devs == array->nr_devs) > - { > - /* We found more members of the array than the array > - actually has according to its superblock. This shouldn't > - happen normally, but what is the sanest things to do in such > - a case? */ > - > - grub_error (GRUB_ERR_BAD_NUMBER, > - "array->nr_devs > array->total_devs (%d)?!?", > - array->total_devs); > - > - return 0; > - } > - > - if (array->device[sb.this_disk.number] != NULL) > - /* We found multiple devices with the same number. Again, > - this shouldn't happen.*/ > - grub_dprintf ("raid", "Found two disks with the number %d?!?", > - sb.this_disk.number); > - } > + if ((p->uuid_len == new_array->uuid_len) && > + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) > + { > + grub_free (new_array->uuid); > + array = p; > + > + /* Do some checks before adding the device to the array. */ > + > + /* FIXME: Check whether the update time of the superblocks are > + the same. */ > + > + if (array->total_devs == array->nr_devs) > + /* We found more members of the array than the array > + actually has according to its superblock. This shouldn't > + happen normally, but what is the sanest things to do in such > + a case? */ > + return grub_error (GRUB_ERR_BAD_NUMBER, > + "array->nr_devs > array->total_devs (%d)?!?", > + array->total_devs); > + > + if (array->device[new_array->index] != NULL) > + /* We found multiple devices with the same number. Again, > + this shouldn't happen.*/ > + return grub_error (GRUB_ERR_BAD_NUMBER, > + "Found two disks with the number %d?!?", > + new_array->number); > + > + break; > + } > > /* Add an array to the list if we didn't find any. */ > if (!array) > { > array = grub_malloc (sizeof (*array)); > if (!array) > - return 0; > - grub_memset (array, 0, sizeof (*array)); > - array->number = sb.md_minor; > - array->version = sb.major_version; > - array->level = sb.level; > - array->layout = sb.layout; > - array->total_devs = sb.nr_disks; > + { > + grub_free (new_array->uuid); > + return grub_errno; > + } > + > + *array = *new_array; > array->nr_devs = 0; > - array->uuid[0] = sb.set_uuid0; > - array->uuid[1] = sb.set_uuid1; > - array->uuid[2] = sb.set_uuid2; > - array->uuid[3] = sb.set_uuid3; > - /* The superblock specifies the size in 1024-byte sectors. */ > - array->disk_size = sb.size * 2; > - array->chunk_size = sb.chunk_size / 512; > + grub_memset (&array->device, 0, sizeof (array->device)); > > /* Check whether we don't have multiple arrays with the same number. */ > for (p = array_list; p != NULL; p = p->next) > - { > - if (p->number == array->number) > - break; > - } > + { > + if (p->number == array->number) > + break; > + } > > if (p) > - { > - /* The number is already in use, so we need to find an new number. */ > - int i = 0; > - > - while (1) > - { > - for (p = array_list; p != NULL; p = p->next) > - { > - if (p->number == i) > - break; > - } > - > - if (!p) > - { > - /* We found an unused number. */ > - array->number = i; > - break; > - } > - > - i++; > - } > - } > + { > + /* The number is already in use, so we need to find an new number. */ > + int i = 0; > + > + while (1) > + { > + for (p = array_list; p != NULL; p = p->next) > + { > + if (p->number == i) > + break; > + } > + > + if (!p) > + { > + /* We found an unused number. */ > + array->number = i; > + break; > + } > + > + i++; > + } > + } > > array->name = grub_malloc (13); > if (! array->name) > - { > - grub_free (array); > + { > + grub_free (array->uuid); > + grub_free (array); > > - return 0; > - } > + return grub_errno; > + } > > grub_sprintf (array->name, "md%d", array->number); > > - grub_dprintf ("raid", "Found array: %s\n", array->name); > + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, > + scanner_name); > > /* Add our new array to the list. */ > array->next = array_list; > @@ -518,47 +441,117 @@ grub_raid_scan_device (const char *name) > } > > /* Add the device to the array. */ > - array->device[sb.this_disk.number] = grub_disk_open (name); > + array->device[new_array->index] = disk; > + array->nr_devs++; > > - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) > + return 0; > +} > + > + > +static grub_raid_t grub_raid_list; > + > +static void > +grub_raid_scan_device (int head_only) > +{ > + auto int hook (const char *name); > + int hook (const char *name) > { > - if (array->total_devs == 1) > - { > - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " > - "doesn't match with size indicated by superblock (0x%llx). " > - "Assuming superblock is wrong.\n", > - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, > - (unsigned long long) array->disk_size); > - array->disk_size = array->device[sb.this_disk.number]->total_sectors; > - } > - else if (array->level == 1) > - { > - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " > - "doesn't match with size indicated by superblock (0x%llx). " > - "Assuming superblock is wrong.\n", > - sb.this_disk.number, > - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, > - (unsigned long long) array->disk_size); > - array->disk_size = array->device[sb.this_disk.number]->total_sectors; > - } > + grub_disk_t disk; > + struct grub_raid_array array; > + struct grub_raid *p; > + > + grub_dprintf ("raid", "Scanning for RAID devices\n"); > + > + disk = grub_disk_open (name); > + if (!disk) > + return 0; > + > + if (disk->total_sectors == ULONG_MAX) Urgh, can you do this cleaner? Actually, why do you do this? > + { > + grub_disk_close (disk); > + return 0; > + } > + > + for (p = grub_raid_list; p; p = p->next) > + { > + if (! p->detect (disk, &array)) > + { > + if (! insert_array (disk, &array, p->name)) > + return 0; > + > + break; > + } > + > + /* This error usually means it's not raid, no need to display > + it. */ > + if (grub_errno != GRUB_ERR_OUT_OF_RANGE) > + grub_print_error (); > + > + grub_errno = GRUB_ERR_NONE; > + if (head_only) > + break; > + } > + > + grub_disk_close (disk); > + > + return 0; > } > > - if (! array->device[sb.this_disk.number]) > + grub_device_iterate (&hook); > +} > + > +static void > +free_array (void) > +{ > + struct grub_raid_array *array; > + > + array = array_list; > + while (array) > { > - /* Remove array from the list if we have just added it. */ > - if (array->nr_devs == 0) > - { > - array_list = array->next; > - grub_free (array->name); > - grub_free (array); > - } > + struct grub_raid_array *p; > + int i; > > - return 0; > + p = array; > + array = array->next; > + > + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) > + if (p->device[i]) > + grub_disk_close (p->device[i]); > + > + grub_free (p->uuid); > + grub_free (p->name); > + grub_free (p); > } > > - array->nr_devs++; > + array_list = 0; > +} > > - return 0; > +void > +grub_raid_register (grub_raid_t raid) > +{ > + raid->next = grub_raid_list; > + grub_raid_list = raid; > + grub_raid_scan_device (1); > +} > + > +void > +grub_raid_unregister (grub_raid_t raid) > +{ > + grub_raid_t *p, q; > + > + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) > + if (q == raid) > + { > + *p = q->next; > + break; > + } > +} > + > +void > +grub_raid_rescan (void) > +{ > + free_array (); > + grub_raid_scan_device (0); > } > > static struct grub_disk_dev grub_raid_dev = > @@ -579,12 +572,11 @@ static struct grub_disk_dev grub_raid_dev = > \f > GRUB_MOD_INIT(raid) > { > - grub_device_iterate (&grub_raid_scan_device); > grub_disk_dev_register (&grub_raid_dev); > } > > GRUB_MOD_FINI(raid) > { > grub_disk_dev_unregister (&grub_raid_dev); > - /* FIXME: free the array list. */ > + free_array (); > } > diff --git a/include/grub/raid.h b/include/grub/raid.h > index 4af97f1..0920f0f 100644 > --- a/include/grub/raid.h > +++ b/include/grub/raid.h > @@ -22,165 +22,42 @@ > > #include <grub/types.h> > > +#define GRUB_RAID_MAX_DEVICES 32 > + > struct grub_raid_array > { > int number; /* The device number, taken from md_minor so we > are consistent with the device name in > Linux. */ > - int version; /* 0 = 0.90, 1 = 1.0 */ > int level; /* RAID levels, only 0, 1 or 5 at the moment. */ > - int layout; /* Only for RAID 5. */ > unsigned int total_devs; /* Total number of devices in the array. */ > - unsigned int nr_devs; /* The number of devices we've found so far. */ > - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ > - grub_uint32_t uuid[4]; /* The UUID of the device. */ > - char *name; /* That will be "md<number>". */ > + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ > grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte > sectors. */ > - grub_disk_t device[32]; /* Array of total_devs devices. */ > + int index; /* Index of current device. */ > + int uuid_len; /* The length of uuid. */ > + char *uuid; /* The UUID of the device. */ > + > + /* The following field is setup by the caller. */ > + char *name; /* That will be "md<number>". */ > + unsigned int nr_devs; /* The number of devices we've found so far. */ > + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ > struct grub_raid_array *next; > }; > > -/* Linux RAID on disk structures and constants, > - copied from include/linux/raid/md_p.h. */ > - > -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) > -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) > - > -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ > - - GRUB_RAID_RESERVED_SECTORS) > - > -#define GRUB_RAID_SB_BYTES 4096 > -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) > -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) > - > -/* > - * The following are counted in 32-bit words > - */ > -#define GRUB_RAID_SB_GENERIC_OFFSET 0 > - > -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 > -#define GRUB_RAID_SB_DISKS_OFFSET 128 > -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 > - > -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 > -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 > -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ > - + GRUB_RAID_SB_GENERIC_STATE_WORDS) > -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 > -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 > -#define GRUB_RAID_SB_DISKS 27 > -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) > -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ > - - GRUB_RAID_SB_PERSONALITY_WORDS \ > - - GRUB_RAID_SB_DISKS_WORDS \ > - - GRUB_RAID_SB_DESCRIPTOR_WORDS) > -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ > - + GRUB_RAID_SB_PERSONALITY_WORDS \ > - + GRUB_RAID_SB_DISKS_WORDS) > - > -/* > - * Device "operational" state bits > - */ > -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ > -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ > -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ > -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ > - > -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. > - * read requests will only be sent here in > - * dire need > - */ > - > - > -#define GRUB_RAID_SB_MAGIC 0xa92b4efc > - > -/* > - * Superblock state bits > - */ > -#define GRUB_RAID_SB_CLEAN 0 > -#define GRUB_RAID_SB_ERRORS 1 > +struct grub_raid > +{ > + const char *name; > > -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ > + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); > > -struct grub_raid_disk_09 { > - grub_uint32_t number; /* 0 Device number in the entire set */ > - grub_uint32_t major; /* 1 Device major number */ > - grub_uint32_t minor; /* 2 Device minor number */ > - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ > - grub_uint32_t state; /* 4 Operational state */ > - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; > + struct grub_raid *next; > }; > +typedef struct grub_raid *grub_raid_t; > > -struct grub_raid_super_09 { > - /* > - * Constant generic information > - */ > - grub_uint32_t md_magic; /* 0 MD identifier */ > - grub_uint32_t major_version; /* 1 major version to which the set conforms */ > - grub_uint32_t minor_version; /* 2 minor version ... */ > - grub_uint32_t patch_version; /* 3 patchlevel version ... */ > - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ > - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ > - grub_uint32_t ctime; /* 6 Creation time */ > - grub_uint32_t level; /* 7 Raid personality */ > - grub_uint32_t size; /* 8 Apparent size of each individual disk */ > - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ > - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ > - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ > - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ > - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ > - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ > - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ > - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; > - > - /* > - * Generic state information > - */ > - grub_uint32_t utime; /* 0 Superblock update time */ > - grub_uint32_t state; /* 1 State bits (clean, ...) */ > - grub_uint32_t active_disks; /* 2 Number of currently active disks */ > - grub_uint32_t working_disks; /* 3 Number of working disks */ > - grub_uint32_t failed_disks; /* 4 Number of failed disks */ > - grub_uint32_t spare_disks; /* 5 Number of spare disks */ > - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ > -#ifdef GRUB_HOST_WORDS_BIGENDIAN > - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ > - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ > - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ > - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ > -#else > - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ > - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ > - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ > - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ > -#endif > - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ > - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; > - > - /* > - * Personality information > - */ > - grub_uint32_t layout; /* 0 the array's physical layout */ > - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ > - grub_uint32_t root_pv; /* 2 LV root PV */ > - grub_uint32_t root_block; /* 3 LV root block */ > - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; > +void grub_raid_register (grub_raid_t raid); > +void grub_raid_unregister (grub_raid_t raid); > > - /* > - * Disks information > - */ > - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; > - > - /* > - * Reserved > - */ > - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; > - > - /* > - * Active descriptor > - */ > - struct grub_raid_disk_09 this_disk; > -}; > +void grub_raid_rescan (void); > > #endif /* ! GRUB_RAID_H */ > diff --git a/util/grub-fstest.c b/util/grub-fstest.c > index 35af6a5..29234ac 100644 > --- a/util/grub-fstest.c > +++ b/util/grub-fstest.c > @@ -29,6 +29,7 @@ > #include <grub/term.h> > #include <grub/mm.h> > #include <grub/normal.h> > +#include <grub/raid.h> > #include <grub/lib/hexdump.h> > > #include <grub_fstest_init.h> > @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) > #define BUF_SIZE 32256 > > static grub_off_t skip, leng; > -static char *part; > > static void > read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) > @@ -273,32 +273,61 @@ cmd_hex (char *pathname) > return 0; > } > > - read_file (pathname, hex_hook); > + if (pathname) > + read_file (pathname, hex_hook); > + else > + { > + char buf[BUF_SIZE]; > + grub_device_t dev; > + > + dev = grub_device_open (0); > + if ((! dev) || (! dev->disk)) > + grub_util_error ("Can\'t open device"); > + > + if (! leng) > + leng = GRUB_DISK_SECTOR_SIZE; > + > + while (leng) > + { > + grub_size_t len; > + > + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; > + > + if (grub_disk_read (dev->disk, 0, skip, len, buf)) > + grub_util_error ("Disk read fails at offset %lld, length %d\n", > + skip, len); > + > + hexdump (skip, buf, len); > + > + skip += len; > + leng -= len; > + } > + > + grub_device_close (dev); > + } > } > > static void > -fstest (char *image_path, int cmd, int n, char **args) > +fstest (char **images, int num_disks, int cmd, int n, char **args) > { > - char host_file[7 + grub_strlen (image_path) + 1]; > - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; > - char *argv[3] = { "-p", "loop", host_file }; > - > - > - grub_sprintf (host_file, "(host)/%s", image_path); > + char host_file[128]; > + char loop_name[8]; > + char *argv[3] = { "-p", loop_name, host_file}; > + int i; > > - if (execute_command (&cmd_loopback, 3, argv)) > + for (i = 0; i < num_disks; i++) > { > - grub_util_error ("loopback command fails.\n"); > - goto fail; > - } > + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) > + grub_util_error ("Pathname %s too long", images[i]); > > - if (part) > - grub_sprintf (device_name, "loop,%s", part); > - else > - grub_strcpy (device_name, "loop"); > + grub_sprintf (loop_name, "loop%d", i); > + grub_sprintf (host_file, "(host)%s", images[i]); > > - grub_env_set ("root", device_name); > + if (execute_command (&cmd_loopback, 3, argv)) > + grub_util_error ("loopback command fails.\n"); > + } > > + grub_raid_rescan (); > switch (cmd) > { > case CMD_LS: > @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) > cmd_cmp (args[0], args[1]); > break; > case CMD_HEX: > - cmd_hex (args[0]); > + cmd_hex ((n == 0) ? 0 : args[0]); > break; > case CMD_BLOCKLIST: > execute_command (&cmd_blocklist, n, args); > grub_printf ("\n"); > } > > -fail: > - > argv[0] = "-d"; > > - execute_command (&cmd_loopback, 2, argv); > + for (i = 0; i < num_disks; i++) > + { > + grub_sprintf (loop_name, "loop%d", i); > + execute_command (&cmd_loopback, 2, argv); > + } > } > > static struct option options[] = { > - {"part", required_argument, 0, 'p'}, > + {"root", required_argument, 0, 'r'}, > {"skip", required_argument, 0, 's'}, > {"length", required_argument, 0, 'n'}, > + {"diskcount", required_argument, 0, 'c'}, > {"debug", required_argument, 0, 'd'}, > - {"raw", no_argument, 0, 'r'}, > - {"long", no_argument, 0, 'l'}, > {"help", no_argument, 0, 'h'}, > {"version", no_argument, 0, 'V'}, > {"verbose", no_argument, 0, 'v'}, > - > {0, 0, 0, 0} > }; > > @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ > ls PATH list files in PATH\n\ > cp SRC DEST copy file to local system\n\ > cmp SRC DEST compare files\n\ > - hex FILE hex dump FILE\n\ > + hex [FILE] Hex dump FILE\n\ > blocklist FILE display blocklist of FILE\n\ > \nOptions:\n\ > - -p, --part=NUM select partition NUM\n\ > + -r, --root=DEVICE_NAME set root device\n\ > -s, --skip=N skip N bytes from output file\n\ > -n, --length=N handle N bytes in output file\n\ > + -c, --diskcount=N N input files\n\ > -d, --debug=S Set debug environment variable\n\ > - -r, --raw disable auto decompression\n\ > - -l, --long show long directory list\n\ > -h, --help display this message and exit\n\ > -V, --version print version information and exit\n\ > -v, --verbose print verbose messages\n\ > @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); > int > main (int argc, char *argv[]) > { > - char *image_path, *debug_str = 0; > - int cmd, is_raw = 0, is_long = 0; > + char *debug_str = 0, *root = 0, *default_root, *alloc_root; > + int i, cmd, num_opts, image_index, num_disks = 1; > > progname = "grub-fstest"; > > + /* Find the first non option entry. */ > + for (num_opts = 1; num_opts < argc; num_opts++) > + if (argv[num_opts][0] == '-') > + { > + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && > + ((argv[num_opts][1] == 'r') || > + (argv[num_opts][1] == 's') || > + (argv[num_opts][1] == 'n') || > + (argv[num_opts][1] == 'c') || > + (argv[num_opts][1] == 'd'))) > + num_opts++; > + } > + else > + break; > + > /* Check for options. */ > while (1) > { > - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); > + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); > + char *p; > > if (c == -1) > break; > else > switch (c) > { > - case 'p': > - part = optarg; > + case 'r': > + root = optarg; > break; > > case 's': > - skip = grub_strtoul (optarg, NULL, 0); > + skip = grub_strtoul (optarg, &p, 0); > + if (*p == 's') > + skip <<= GRUB_DISK_SECTOR_BITS; > break; > > case 'n': > - leng = grub_strtoul (optarg, NULL, 0); > + leng = grub_strtoul (optarg, &p, 0); > + if (*p == 's') > + leng <<= GRUB_DISK_SECTOR_BITS; > break; > > + case 'c': > + num_disks = grub_strtoul (optarg, NULL, 0); > + if (num_disks < 1) > + { > + fprintf (stderr, "Invalid disk count.\n"); > + usage (1); > + } > + break; > + > case 'd': > debug_str = optarg; > break; > > - case 'r': > - is_raw = 1; > - break; > - > - case 'l': > - is_long = 1; > - break; > - > case 'h': > usage (0); > break; > @@ -432,35 +481,29 @@ main (int argc, char *argv[]) > } > > /* Obtain PATH. */ > - if (optind >= argc) > - { > - fprintf (stderr, "No path is specified.\n"); > - usage (1); > - } > - > - image_path = argv[optind]; > - > - if (*image_path != '/') > + if (optind + num_disks - 1 >= argc) > { > - fprintf (stderr, "Must use absolute path.\n"); > + fprintf (stderr, "Not enough pathname.\n"); > usage (1); > } > > - optind++; > + image_index = optind; > + for (i = 0; i < num_disks; i++, optind++) > + if (argv[optind][0] != '/') > + { > + fprintf (stderr, "Must use absolute path.\n"); > + usage (1); > + } > > cmd = 0; > if (optind < argc) > { > - int nparm = 1; > + int nparm = 0; > > if (!grub_strcmp (argv[optind], "ls")) > - { > - cmd = CMD_LS; > - if (is_long) > - argv[optind--] = "-l"; > - else > - nparm = 0; > - } > + { > + cmd = CMD_LS; > + } > else if (!grub_strcmp (argv[optind], "cp")) > { > cmd = CMD_CP; > @@ -478,6 +521,7 @@ main (int argc, char *argv[]) > else if (!grub_strcmp (argv[optind], "blocklist")) > { > cmd = CMD_BLOCKLIST; > + nparm = 1; > } > else > { > @@ -503,14 +547,31 @@ main (int argc, char *argv[]) > /* Initialize all modules. */ > grub_init_all (); > > - if (is_raw) > - grub_env_set ("filehook", "0"); > - > if (debug_str) > grub_env_set ("debug", debug_str); > > + default_root = (num_disks == 1) ? "loop0" : "md0"; > + alloc_root = 0; > + if (root) > + { > + if ((*root >= '0') && (*root <= '9')) > + { > + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); > + > + sprintf (alloc_root, "%s,%s", default_root, root); > + root = alloc_root; > + } > + } > + else > + root = default_root; > + > + grub_env_set ("root", root); > + > + if (alloc_root) > + free (alloc_root); > + > /* Do it. */ > - fstest (image_path + 1, cmd, argc - optind, argv + optind); > + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); > > /* Free resources. */ > grub_fini_all (); > diff --git a/util/grub-probe.c b/util/grub-probe.c > index a4f51e2..ce9cbff 100644 > --- a/util/grub-probe.c > +++ b/util/grub-probe.c > @@ -142,7 +142,7 @@ probe (const char *path, char *device_name) > abstraction_name = "lvm"; > break; > case GRUB_DEV_ABSTRACTION_RAID: > - abstraction_name = "raid"; > + abstraction_name = "raid mdraid"; > break; > default: > grub_util_info ("did not find LVM/RAID in %s, assuming raw device", device_name); > _______________________________________________ > Grub-devel mailing list > Grub-devel@gnu.org > http://lists.gnu.org/mailman/listinfo/grub-devel ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-13 10:05 ` Marco Gerards @ 2008-08-13 18:43 ` Bean 2008-08-13 20:01 ` Bean 0 siblings, 1 reply; 24+ messages in thread From: Bean @ 2008-08-13 18:43 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 177 bytes --] Hi, This new patch add support for four different layout right/left asymmetric/symmetric, and support raid4 and raid6. It also unify the handling of raid 0, 4, 5, 6. -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_7.diff --] [-- Type: text/x-diff; name=raid_7.diff, Size: 64250 bytes --] diff --git a/conf/common.rmk b/conf/common.rmk index 95859f7..3876ce4 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -15,7 +15,7 @@ grub_probe_SOURCES = util/grub-probe.c \ \ partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c \ - disk/lvm.c disk/raid.c grub_probe_init.c + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c ifeq ($(enable_grub_fstest), yes) bin_UTILITIES += grub-fstest @@ -35,6 +35,7 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c # For the parser. @@ -264,7 +265,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod lvm.mod mdraid.mod dm_nv.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -276,6 +277,16 @@ lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk index 606b99c..74808ca 100644 --- a/conf/i386-coreboot.rmk +++ b/conf/i386-coreboot.rmk @@ -92,7 +92,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk index 2ce21b1..705e4ef 100644 --- a/conf/i386-efi.rmk +++ b/conf/i386-efi.rmk @@ -62,7 +62,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index a93845e..e996865 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -90,7 +90,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index c1e4ac4..f3142e4 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -102,7 +102,7 @@ grub_setup_SOURCES = util/i386/pc/grub-setup.c util/biosdisk.c \ \ partmap/pc.c partmap/gpt.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/lvm.c \ util/raid.c util/lvm.c \ grub_setup_init.c @@ -142,7 +142,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk index 0ed75f3..15e2fae 100644 --- a/conf/powerpc-ieee1275.rmk +++ b/conf/powerpc-ieee1275.rmk @@ -73,7 +73,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/powerpc/ieee1275/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_script.tab.c grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk index 4f8abba..8553226 100644 --- a/conf/x86_64-efi.rmk +++ b/conf/x86_64-efi.rmk @@ -64,7 +64,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c new file mode 100644 index 0000000..82eb26a --- /dev/null +++ b/disk/dmraid_nvidia.c @@ -0,0 +1,165 @@ +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +#define NV_SIGNATURES 4 + +#define NV_IDLE 0 +#define NV_SCDB_INIT_RAID 2 +#define NV_SCDB_REBUILD_RAID 3 +#define NV_SCDB_UPGRADE_RAID 4 +#define NV_SCDB_SYNC_RAID 5 + +#define NV_LEVEL_UNKNOWN 0x00 +#define NV_LEVEL_JBOD 0xFF +#define NV_LEVEL_0 0x80 +#define NV_LEVEL_1 0x81 +#define NV_LEVEL_3 0x83 +#define NV_LEVEL_5 0x85 +#define NV_LEVEL_10 0x8a +#define NV_LEVEL_1_0 0x8180 + +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ + +struct grub_raid_nv_array +{ + grub_uint32_t version; + grub_uint32_t signature[NV_SIGNATURES]; + grub_uint8_t raid_job_code; + grub_uint8_t stripe_width; + grub_uint8_t total_volumes; + grub_uint8_t original_width; + grub_uint32_t raid_level; + grub_uint32_t stripe_block_size; + grub_uint32_t stripe_block_size_bytes; + grub_uint32_t stripe_block_size_log2; + grub_uint32_t stripe_mask; + grub_uint32_t stripe_size; + grub_uint32_t stripe_size_bytes; + grub_uint32_t raid_job_mask; + grub_uint32_t original_capacity; + grub_uint32_t flags; +}; + +#define NV_ID_LENGTH 8 +#define NV_ID_STRING "NVIDIA" +#define NV_VERSION 100 + +#define NV_PRODUCTIDS 16 +#define NV_PRODUCTREVISIONS 4 + +struct grub_raid_nv_super +{ + grub_uint8_t vendor[NV_ID_LENGTH]; /* 0x00 - 0x07 ID string. */ + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ + grub_uint8_t reserved; /* 0x13. */ + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ + grub_uint8_t product_id[NV_PRODUCTIDS]; /* 0x1C - 0x2B Array product ID. */ + grub_uint8_t product_rev[NV_PRODUCTREVISIONS]; /* 0x2C - 0x2F Array product revision */ + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ + struct grub_raid_nv_array array; /* Array information */ +} __attribute__ ((packed)); + +static grub_err_t +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + struct grub_raid_nv_super sb; + grub_uint32_t *uuid; + + if (disk->partition) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "skip partition"); + + sector = grub_disk_get_size (disk) - 2; + + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) + return grub_errno; + + if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + if (sb.version != NV_VERSION) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unknown version: %d.%d", sb.version); + + switch (sb.array.raid_level) + { + case NV_LEVEL_0: + array->level = 0; + array->disk_size = sb.capacity / sb.array.total_volumes; + break; + + case NV_LEVEL_1: + array->level = 1; + array->disk_size = sb.capacity; + break; + + case NV_LEVEL_5: + array->level = 5; + array->disk_size = sb.capacity / (sb.array.total_volumes - 1); + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.array.raid_level); + } + + array->number = 0; + array->total_devs = sb.array.total_volumes; + array->chunk_size = sb.array.stripe_block_size; + array->index = sb.unit_number; + array->uuid_len = sizeof (sb.array.signature); + array->uuid = grub_malloc (sizeof (sb.array.signature)); + if (! array->uuid) + return grub_errno; + + grub_memcpy (array->uuid, (char *) &sb.array.signature, + sizeof (sb.array.signature)); + + return 0; +} + +static struct grub_raid grub_dmraid_nv_dev = +{ + .name = "dmraid_nv", + .detect = grub_dmraid_nv_detect, + .next = 0 +}; + +GRUB_MOD_INIT(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} + +GRUB_MOD_FINI(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..8b7f5ed 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; - total_sectors = 9000000; /* TODO: get the correct size. */ + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c new file mode 100644 index 0000000..2306972 --- /dev/null +++ b/disk/mdraid_linux.c @@ -0,0 +1,243 @@ +/* mdraid_linux.c - module to handle linux softraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define GRUB_RAID_RESERVED_BYTES (64 * 1024) +#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) + +#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ + - GRUB_RAID_RESERVED_SECTORS) + +#define GRUB_RAID_SB_BYTES 4096 +#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) +#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define GRUB_RAID_SB_GENERIC_OFFSET 0 + +#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 +#define GRUB_RAID_SB_DISKS_OFFSET 128 +#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 + +#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 +#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 +#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ + + GRUB_RAID_SB_GENERIC_STATE_WORDS) +#define GRUB_RAID_SB_PERSONALITY_WORDS 64 +#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 +#define GRUB_RAID_SB_DISKS 27 +#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ + - GRUB_RAID_SB_PERSONALITY_WORDS \ + - GRUB_RAID_SB_DISKS_WORDS \ + - GRUB_RAID_SB_DESCRIPTOR_WORDS) +#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ + + GRUB_RAID_SB_PERSONALITY_WORDS \ + + GRUB_RAID_SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ +#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ +#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ +#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + +#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. + * read requests will only be sent here in + * dire need + */ + + +#define GRUB_RAID_SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define GRUB_RAID_SB_CLEAN 0 +#define GRUB_RAID_SB_ERRORS 1 + +#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + +struct grub_raid_disk_09 { + grub_uint32_t number; /* 0 Device number in the entire set */ + grub_uint32_t major; /* 1 Device major number */ + grub_uint32_t minor; /* 2 Device minor number */ + grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ + grub_uint32_t state; /* 4 Operational state */ + grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 { + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* 0 MD identifier */ + grub_uint32_t major_version; /* 1 major version to which the set conforms */ + grub_uint32_t minor_version; /* 2 minor version ... */ + grub_uint32_t patch_version; /* 3 patchlevel version ... */ + grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ + grub_uint32_t set_uuid0; /* 5 Raid set identifier */ + grub_uint32_t ctime; /* 6 Creation time */ + grub_uint32_t level; /* 7 Raid personality */ + grub_uint32_t size; /* 8 Apparent size of each individual disk */ + grub_uint32_t nr_disks; /* 9 total disks in the raid set */ + grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ + grub_uint32_t md_minor; /* 11 preferred MD minor device number */ + grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ + grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ + grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ + grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ + grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* 0 Superblock update time */ + grub_uint32_t state; /* 1 State bits (clean, ...) */ + grub_uint32_t active_disks; /* 2 Number of currently active disks */ + grub_uint32_t working_disks; /* 3 Number of working disks */ + grub_uint32_t failed_disks; /* 4 Number of failed disks */ + grub_uint32_t spare_disks; /* 5 Number of spare disks */ + grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ +#ifdef GRUB_HOST_WORDS_BIGENDIAN + grub_uint32_t events_hi; /* 7 high-order of superblock update count */ + grub_uint32_t events_lo; /* 8 low-order of superblock update count */ + grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ + grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ +#else + grub_uint32_t events_lo; /* 7 low-order of superblock update count */ + grub_uint32_t events_hi; /* 8 high-order of superblock update count */ + grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ + grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ +#endif + grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ + grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* 0 the array's physical layout */ + grub_uint32_t chunk_size; /* 1 chunk size in bytes */ + grub_uint32_t root_pv; /* 2 LV root PV */ + grub_uint32_t root_block; /* 3 LV root block */ + grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +}; + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = GRUB_RAID_NEW_SIZE_SECTORS(size); + + if (grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != GRUB_RAID_SB_MAGIC) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + if (sb.level != 0 && sb.level != 1 && sb.level != 4 && + sb.level != 5 && sb.level != 6) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", + sb.level); + + if (sb.level >= 5 && sb.layout > GRUB_RAID_LAYOUT_RIGHT_SYMMETRIC) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID5/6 layout: %d", + sb.layout); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = sb.layout; + array->total_devs = sb.nr_disks; + array->disk_size = (sb.size) ? sb.size * 2 : sector; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (! array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = +{ + .name = "mdraid", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} + +GRUB_MOD_FINI(mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 163bd81..4bbc814 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -43,10 +43,15 @@ grub_is_array_readable (struct grub_raid_array *array) return 1; break; + case 4: case 5: if (array->nr_devs >= array->total_devs - 1) return 1; break; + + case 6: + if (array->nr_devs >= array->total_devs - 2) + return 1; } return 0; @@ -91,6 +96,8 @@ static grub_err_t grub_raid_open (const char *name, grub_disk_t disk) { struct grub_raid_array *array; + grub_disk_addr_t disk_size; + unsigned i, n; for (array = array_list; array != NULL; array = array->next) { @@ -106,22 +113,28 @@ grub_raid_open (const char *name, grub_disk_t disk) disk->id = array->number; disk->data = array; + /* Find the smallest disk. */ + disk_size = ULONG_MAX; + for (i = 0; i < array->total_devs; i++) + if ((array->device[i]) && (array->device[i]->total_sectors < disk_size)) + disk_size = array->device[i]->total_sectors; + grub_dprintf ("raid", "%s: total_devs=%d, disk_size=%lld\n", name, - array->total_devs, (unsigned long long) array->disk_size); + array->total_devs, (unsigned long long) disk_size); switch (array->level) { - case 0: - /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; - break; - case 1: - disk->total_sectors = array->disk_size; + disk->total_sectors = disk_size; break; + case 0: + case 4: case 5: - disk->total_sectors = (array->total_devs - 1) * array->disk_size; + case 6: + n = array->level / 3; + + disk->total_sectors = (array->total_devs - n) * disk_size; break; } @@ -137,75 +150,255 @@ grub_raid_close (grub_disk_t disk __attribute ((unused))) return; } +static void +block_xor (char *buf1, char *buf2, int size) +{ + grub_size_t *p1, *p2; + + p1 = (grub_size_t *) buf1; + p2 = (grub_size_t *) buf2; + size /= GRUB_CPU_SIZEOF_VOID_P; + + while (size) + { + *(p1++) ^= *(p2++); + size--; + } +} + static grub_err_t -grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) +grub_raid5_recover (struct grub_raid_array *array, int disknr, + char *buf, grub_disk_addr_t sector, int size) { - struct grub_raid_array *array = disk->data; - grub_err_t err = 0; + char *buf2; + int i; - switch (array->level) + size <<= GRUB_DISK_SECTOR_BITS; + buf2 = grub_malloc (size); + if (!buf2) + return grub_errno; + + grub_memset (buf, 0, size); + + for (i = 0; i < (int) array->total_devs; i++) { - case 0: - { - grub_uint64_t a; - grub_uint32_t b; - unsigned int disknr; - grub_disk_addr_t read_sector; - grub_size_t read_size; + grub_err_t err; - /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, array->total_devs, &disknr); + if (i == disknr) + continue; - a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; + err = grub_disk_read (array->device[i], sector, 0, size, buf2); - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; + if (err) + { + grub_free (buf2); + return err; + } - if (read_size > size) - read_size = size; + block_xor (buf, buf2, size); + } - while (1) - { - grub_uint32_t i; + grub_free (buf2); - err = grub_disk_read (array->device[disknr], read_sector, 0, - read_size << GRUB_DISK_SECTOR_BITS, buf); - if (err) - break; + return GRUB_ERR_NONE; +} - buf += read_size << GRUB_DISK_SECTOR_BITS; - size -= read_size; - if (! size) - break; +static grub_uint8_t raid6_table1[256][256]; +static grub_uint8_t raid6_table2[256][256]; +static int raid6_inited; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; +static void +block_mul (grub_uint8_t mul, char *buf, int size) +{ + int i; + grub_uint8_t *p; - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; + p = buf; + for (i = 0; i < size; i++, p++) + *p = raid6_table1[mul][*p]; +} - disknr++; - /* See whether the disk was the last disk, and start - reading from the first disk in that case. */ - if (disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - } - } +static void +grub_raid6_init_table (void) +{ + int i, j; + + for (i = 0; i < 256; i++) + raid6_table1[i][1] = raid6_table1[1][i] = i; + + for (i = 2; i < 256; i++) + for (j = i; j < 256; j++) + { + int n; + grub_uint8_t c; + + n = i >> 1; + + c = raid6_table1[n][j]; + c = (c << 1) ^ ((c & 0x80) ? 0x1d : 0); + if (i & 1) + c ^= j; + + raid6_table1[j][i] = raid6_table1[i][j] = c; } - break; + raid6_table2[0][0] = 1; + for (i = 1; i < 256; i++) + raid6_table2[i][i] = raid6_table1[raid6_table2[i - 1][i - 1]][2]; + + for (i = 0; i < 254; i++) + for (j = i + 1; j < 255; j++) + { + grub_uint8_t n, c; + int k; + + n = raid6_table2[j - i][j - i] ^ 1; + c = n; + for (k = 0; k < 253; k++) + c = raid6_table1[c][n]; + + raid6_table2[j][i] = c; + + n = raid6_table2[255 + i - j][255 + i - j] ^ 1; + c = n; + for (k = 0; k < 253; k++) + c = raid6_table1[c][n]; + + raid6_table2[i][j] = c; + } + + raid6_inited = 1; +} + +static grub_err_t +grub_raid6_recover (struct grub_raid_array *array, int disknr, int p, + char *buf, grub_disk_addr_t sector, int size) +{ + int i, q, pos; + int err[2], nerr; + char *pbuf = 0, *qbuf = 0; + + size <<= GRUB_DISK_SECTOR_BITS; + pbuf = grub_malloc (size); + if (!pbuf) + goto quit; + + qbuf = grub_malloc (size); + if (!qbuf) + goto quit; + + q = p + 1; + if (q == (int) array->total_devs) + q = 0; + + grub_memset (pbuf, 0, size); + grub_memset (qbuf, 0, size); + + pos = q + 1; + if (pos == (int) array->total_devs) + pos = 0; + + nerr = 1; + + //grub_printf ("P: %d %d %d %d\n", disknr, p, q, pos); + + for (i = 0; i < (int) array->total_devs - 2; i++) + { + if (pos == disknr) + err[0] = i; + else + { + if ((array->device[pos]) && + (! grub_disk_read (array->device[pos], sector, 0, size, buf))) + { + block_xor (pbuf, buf, size); + block_mul (raid6_table2[i][i], buf, size); + block_xor (qbuf, buf, size); + } + else + { + if (nerr >= 2) + goto quit; + + err[nerr++] = i; + grub_errno = GRUB_ERR_NONE; + } + } + + pos++; + if (pos == (int) array->total_devs) + pos = 0; + } + + if (nerr == 1) + { + if ((array->device[p]) && + (! grub_disk_read (array->device[p], sector, 0, size, buf))) + { + block_xor (buf, pbuf, size); + goto quit; + } + + if (! array->device[q]) + { + grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore"); + goto quit; + } + + grub_errno = GRUB_ERR_NONE; + if (grub_disk_read (array->device[q], sector, 0, size, buf)) + goto quit; + + block_xor (buf, qbuf, size); + block_mul (raid6_table2[255 - err[0]][255 - err[0]], buf, size); + } + else + { + grub_uint8_t c; + + if ((! array->device[p]) || (! array->device[q])) + { + grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore"); + goto quit; + } + + if (grub_disk_read (array->device[p], sector, 0, size, buf)) + goto quit; + + block_xor (pbuf, buf, size); + + if (grub_disk_read (array->device[q], sector, 0, size, buf)) + goto quit; + + block_xor (qbuf, buf, size); + + c = raid6_table2[err[1]][err[0]]; + c = raid6_table1[raid6_table2[255 - err[0]][255 - err[0]]][c]; + block_mul (c, qbuf, size); + + c = raid6_table1[raid6_table2[err[1]][err[1]]][c]; + block_mul (c, pbuf, size); + + block_xor (pbuf, qbuf, size); + grub_memcpy (buf, pbuf, size); + } + +quit: + grub_free (pbuf); + grub_free (qbuf); + + return grub_errno; +} + +static grub_err_t +grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + struct grub_raid_array *array = disk->data; + grub_err_t err = 0; + + switch (array->level) + { case 1: /* This is easy, we can read from any disk we want. We will loop over all disks until we've found one that is available. In @@ -217,6 +410,10 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, { if (array->device[i]) { + /* Reset read error. */ + if (grub_errno == GRUB_ERR_READ_ERROR) + grub_errno = GRUB_ERR_NONE; + err = grub_disk_read (array->device[i], sector, 0, size << GRUB_DISK_SECTOR_BITS, buf); @@ -227,110 +424,137 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, } break; + case 0: + case 4: case 5: + case 6: { - grub_uint64_t a; - grub_uint32_t b; - int disknr; grub_disk_addr_t read_sector; - grub_size_t read_size; - - /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, (array->total_devs - 1), &b); - disknr = b; - - a = grub_divmod64 (sector, array->chunk_size * (array->total_devs - 1), - NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; - - grub_divmod64 (read_sector, array->chunk_size * array->total_devs, &b); - disknr -= (b / array->chunk_size); - if (disknr < 0) - disknr += array->total_devs; + grub_uint32_t b, p, n, disknr, e; - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; + /* n = 0 for level 0, 1 for level 4 and 5, 2 for level 6. */ + n = array->level / 3; - if (read_size > size) - read_size = size; + /* Find the first sector to read. */ + read_sector = grub_divmod64 (sector, array->chunk_size, &b); + read_sector = grub_divmod64 (read_sector, array->total_devs - n, + &disknr); + if (array->level >= 5) + { + grub_divmod64 (read_sector, array->total_devs, &p); + + if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)) + p = array->total_devs - 1 - p; + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr += p + n; + if (disknr >= array->total_devs) + disknr -= array->total_devs; + } + else + { + if (disknr >= p) + disknr += n; + } + } + else + p = array->total_devs - n; + + read_sector *= array->chunk_size; while (1) { - grub_uint32_t i; + grub_size_t read_size; + int next_level; + + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + e = 0; + if (array->device[disknr]) + { + /* Reset read error. */ + if (grub_errno == GRUB_ERR_READ_ERROR) + grub_errno = GRUB_ERR_NONE; + + err = grub_disk_read (array->device[disknr], + read_sector + b, 0, + read_size << GRUB_DISK_SECTOR_BITS, + buf); + + if ((err) && (err != GRUB_ERR_READ_ERROR)) + break; + e++; + } + else + err = GRUB_ERR_READ_ERROR; - if (array->device[disknr]) - err = grub_disk_read (array->device[disknr], read_sector, 0, - read_size << GRUB_DISK_SECTOR_BITS, buf); + if (err) + { + if (array->nr_devs < array->total_devs - n + e) + break; - /* If an error occurs when we already have an degraded - array we can't recover from that. */ - if (err && ((array->total_devs - 1) == array->nr_devs)) - break; + grub_errno = GRUB_ERR_NONE; + err = ((array->level == 6) ? + grub_raid6_recover (array, disknr, p, + buf, read_sector + b, read_size) : + grub_raid5_recover (array, disknr, + buf, read_sector + b, read_size)); - if (err || ! array->device[disknr]) - { - /* Either an error occured or the disk is not - available. We have to compute this block from the - blocks on the other hard disks. */ - grub_size_t buf_size = read_size << GRUB_DISK_SECTOR_BITS; - char buf2[buf_size]; - unsigned int j; - - grub_memset (buf, 0, buf_size); - - for (j = 0; j < array->total_devs; j++) - { - unsigned int k; - - if (j != (unsigned int) disknr) - { - err = grub_disk_read (array->device[j], read_sector, - 0, buf_size, buf2); - if (err) - return err; - - for (k = 0; k < buf_size; k++) - buf[k] = buf[k] ^ buf2[k]; - } - } - } + if (err) + break; + } - buf += (read_size << GRUB_DISK_SECTOR_BITS); + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - + b = 0; disknr++; - grub_divmod64 (read_sector, - array->chunk_size * array->total_devs, &i); - if ((unsigned int) disknr == (array->total_devs - (i / array->chunk_size) - 1)) - disknr++; - /* See whether the disk was the last disk, and start - reading from the first disk in that case. */ - if ((unsigned int) disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - grub_divmod64 (read_sector, - array->chunk_size * array->total_devs, &i); - if ((i / array->chunk_size) == (array->total_devs - 1)) - disknr++; - } + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + if (disknr == array->total_devs) + disknr = 0; + + next_level = (disknr == p); + } + else + { + if (disknr == p) + disknr += n; + + next_level = (disknr >= array->total_devs); + } + + if (next_level) + { + read_sector += array->chunk_size; + + if (array->level >= 5) + { + if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK) + p = (p == array->total_devs - 1) ? 0 : p + 1; + else + p = (p == 0) ? array->total_devs - 1 : p - 1; + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr += (n - 1); + if (disknr >= array->total_devs) + disknr -= array->total_devs; + } + else + { + disknr -= array->total_devs; + if (disknr == p) + disknr += n; + } + } + } } } break; @@ -348,169 +572,103 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *scanner_name) { - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); - - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } - - /* FIXME: Check the checksum. */ - - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } - - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } array->name = grub_malloc (13); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } grub_sprintf (array->name, "md%d", array->number); - grub_dprintf ("raid", "Found array: %s\n", array->name); + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, + scanner_name); /* Add our new array to the list. */ array->next = array_list; @@ -518,47 +676,121 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; + + return 0; +} - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) +static grub_raid_t grub_raid_list; + +static void +grub_raid_scan_device (int head_only) +{ + auto int hook (const char *name); + int hook (const char *name) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); + + disk = grub_disk_open (name); + if (!disk) + return 0; + + if (disk->total_sectors == ULONG_MAX) + { + grub_disk_close (disk); + return 0; + } + + for (p = grub_raid_list; p; p = p->next) + { + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + { + if ((array.level == 6) && (! raid6_inited)) + grub_raid6_init_table (); + + return 0; + } + + break; + } + + /* This error usually means it's not raid, no need to display + it. */ + if (grub_errno != GRUB_ERR_OUT_OF_RANGE) + grub_print_error (); + + grub_errno = GRUB_ERR_NONE; + if (head_only) + break; + } + + grub_disk_close (disk); + + return 0; } - if (! array->device[sb.this_disk.number]) + grub_device_iterate (&hook); +} + +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + struct grub_raid_array *p; + int i; - return 0; + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); } - array->nr_devs++; + array_list = 0; +} - return 0; +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; + grub_raid_scan_device (1); +} + +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; + + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} + +void +grub_raid_rescan (void) +{ + free_array (); + grub_raid_scan_device (0); } static struct grub_disk_dev grub_raid_dev = @@ -579,12 +811,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..81eb582 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,51 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + +#define GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC 0 +#define GRUB_RAID_LAYOUT_RIGHT_ASYMMETRIC 1 +#define GRUB_RAID_LAYOUT_LEFT_SYMMETRIC 2 +#define GRUB_RAID_LAYOUT_RIGHT_SYMMETRIC 3 + +#define GRUB_RAID_LAYOUT_RIGHT_MASK 1 +#define GRUB_RAID_LAYOUT_SYMMETRIC_MASK 2 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ - int layout; /* Only for RAID 5. */ + int layout; /* Layout for RAID 5/6. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) - -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) - -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ - -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ - - -#define GRUB_RAID_SB_MAGIC 0xa92b4efc - -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 +struct grub_raid +{ + const char *name; -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; + struct grub_raid *next; }; +typedef struct grub_raid *grub_raid_t; -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +void grub_raid_rescan (void); #endif /* ! GRUB_RAID_H */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..29234ac 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,6 +29,7 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> #include <grub_fstest_init.h> @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -273,32 +273,61 @@ cmd_hex (char *pathname) return 0; } - read_file (pathname, hex_hook); + if (pathname) + read_file (pathname, hex_hook); + else + { + char buf[BUF_SIZE]; + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device"); + + if (! leng) + leng = GRUB_DISK_SECTOR_SIZE; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d\n", + skip, len); + + hexdump (skip, buf, len); + + skip += len; + leng -= len; + } + + grub_device_close (dev); + } } static void -fstest (char *image_path, int cmd, int n, char **args) +fstest (char **images, int num_disks, int cmd, int n, char **args) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; - - - grub_sprintf (host_file, "(host)/%s", image_path); + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails.\n"); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) cmd_cmp (args[0], args[1]); break; case CMD_HEX: - cmd_hex (args[0]); + cmd_hex ((n == 0) ? 0 : args[0]); break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ ls PATH list files in PATH\n\ cp SRC DEST copy file to local system\n\ cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + hex [FILE] Hex dump FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,35 +481,29 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) - { - fprintf (stderr, "No path is specified.\n"); - usage (1); - } - - image_path = argv[optind]; - - if (*image_path != '/') + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "Must use absolute path.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; @@ -478,6 +521,7 @@ main (int argc, char *argv[]) else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +547,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); diff --git a/util/grub-probe.c b/util/grub-probe.c index a4f51e2..ce9cbff 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -142,7 +142,7 @@ probe (const char *path, char *device_name) abstraction_name = "lvm"; break; case GRUB_DEV_ABSTRACTION_RAID: - abstraction_name = "raid"; + abstraction_name = "raid mdraid"; break; default: grub_util_info ("did not find LVM/RAID in %s, assuming raw device", device_name); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-13 18:43 ` Bean @ 2008-08-13 20:01 ` Bean 2008-08-16 9:07 ` Bean 0 siblings, 1 reply; 24+ messages in thread From: Bean @ 2008-08-13 20:01 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 395 bytes --] On Thu, Aug 14, 2008 at 2:43 AM, Bean <bean123ch@gmail.com> wrote: > Hi, > > This new patch add support for four different layout right/left > asymmetric/symmetric, and support raid4 and raid6. It also unify the > handling of raid 0, 4, 5, 6. Hi, This new patch cleanup the macro and structure from old raid.h, and add support for asymmetric layout when recovering from raid6 error. -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_8.diff --] [-- Type: text/x-diff; name=raid_8.diff, Size: 63366 bytes --] diff --git a/conf/common.rmk b/conf/common.rmk index 95859f7..3876ce4 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -15,7 +15,7 @@ grub_probe_SOURCES = util/grub-probe.c \ \ partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c \ - disk/lvm.c disk/raid.c grub_probe_init.c + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c ifeq ($(enable_grub_fstest), yes) bin_UTILITIES += grub-fstest @@ -35,6 +35,7 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c # For the parser. @@ -264,7 +265,7 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod lvm.mod mdraid.mod dm_nv.mod # For raid.mod raid_mod_SOURCES = disk/raid.c @@ -276,6 +277,16 @@ lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + # Commands. pkglib_MODULES += hello.mod boot.mod terminal.mod ls.mod \ cmp.mod cat.mod help.mod font.mod search.mod \ diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk index 606b99c..74808ca 100644 --- a/conf/i386-coreboot.rmk +++ b/conf/i386-coreboot.rmk @@ -92,7 +92,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk index 2ce21b1..705e4ef 100644 --- a/conf/i386-efi.rmk +++ b/conf/i386-efi.rmk @@ -62,7 +62,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index a93845e..e996865 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -90,7 +90,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index c1e4ac4..f3142e4 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -102,7 +102,7 @@ grub_setup_SOURCES = util/i386/pc/grub-setup.c util/biosdisk.c \ \ partmap/pc.c partmap/gpt.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/lvm.c \ util/raid.c util/lvm.c \ grub_setup_init.c @@ -142,7 +142,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk index 0ed75f3..15e2fae 100644 --- a/conf/powerpc-ieee1275.rmk +++ b/conf/powerpc-ieee1275.rmk @@ -73,7 +73,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/powerpc/ieee1275/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_script.tab.c grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk index 4f8abba..8553226 100644 --- a/conf/x86_64-efi.rmk +++ b/conf/x86_64-efi.rmk @@ -64,7 +64,7 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c new file mode 100644 index 0000000..c2d5ed3 --- /dev/null +++ b/disk/dmraid_nvidia.c @@ -0,0 +1,166 @@ +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +#define NV_SIGNATURES 4 + +#define NV_IDLE 0 +#define NV_SCDB_INIT_RAID 2 +#define NV_SCDB_REBUILD_RAID 3 +#define NV_SCDB_UPGRADE_RAID 4 +#define NV_SCDB_SYNC_RAID 5 + +#define NV_LEVEL_UNKNOWN 0x00 +#define NV_LEVEL_JBOD 0xFF +#define NV_LEVEL_0 0x80 +#define NV_LEVEL_1 0x81 +#define NV_LEVEL_3 0x83 +#define NV_LEVEL_5 0x85 +#define NV_LEVEL_10 0x8a +#define NV_LEVEL_1_0 0x8180 + +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ + +struct grub_nv_array +{ + grub_uint32_t version; + grub_uint32_t signature[NV_SIGNATURES]; + grub_uint8_t raid_job_code; + grub_uint8_t stripe_width; + grub_uint8_t total_volumes; + grub_uint8_t original_width; + grub_uint32_t raid_level; + grub_uint32_t stripe_block_size; + grub_uint32_t stripe_block_size_bytes; + grub_uint32_t stripe_block_size_log2; + grub_uint32_t stripe_mask; + grub_uint32_t stripe_size; + grub_uint32_t stripe_size_bytes; + grub_uint32_t raid_job_mask; + grub_uint32_t original_capacity; + grub_uint32_t flags; +}; + +#define NV_ID_LEN 8 +#define NV_ID_STRING "NVIDIA" +#define NV_VERSION 100 + +#define NV_PRODID_LEN 16 +#define NV_PRODREV_LEN 4 + +struct grub_nv_super +{ + char vendor[NV_ID_LEN]; /* 0x00 - 0x07 ID string. */ + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ + grub_uint8_t reserved; /* 0x13. */ + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ + char prodid[NV_PRODID_LEN]; /* 0x1C - 0x2B Array product ID. */ + char prodrev[NV_PRODREV_LEN]; /* 0x2C - 0x2F Array product revision */ + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ + struct grub_nv_array array; /* Array information */ +} __attribute__ ((packed)); + +static grub_err_t +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + struct grub_nv_super sb; + grub_uint32_t *uuid; + + if (disk->partition) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "skip partition"); + + sector = grub_disk_get_size (disk) - 2; + + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) + return grub_errno; + + if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + if (sb.version != NV_VERSION) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unknown version: %d.%d", sb.version); + + switch (sb.array.raid_level) + { + case NV_LEVEL_0: + array->level = 0; + array->disk_size = sb.capacity / sb.array.total_volumes; + break; + + case NV_LEVEL_1: + array->level = 1; + array->disk_size = sb.capacity; + break; + + case NV_LEVEL_5: + array->level = 5; + array->layout = GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC; + array->disk_size = sb.capacity / (sb.array.total_volumes - 1); + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.array.raid_level); + } + + array->number = 0; + array->total_devs = sb.array.total_volumes; + array->chunk_size = sb.array.stripe_block_size; + array->index = sb.unit_number; + array->uuid_len = sizeof (sb.array.signature); + array->uuid = grub_malloc (sizeof (sb.array.signature)); + if (! array->uuid) + return grub_errno; + + grub_memcpy (array->uuid, (char *) &sb.array.signature, + sizeof (sb.array.signature)); + + return 0; +} + +static struct grub_raid grub_dmraid_nv_dev = +{ + .name = "dmraid_nv", + .detect = grub_dmraid_nv_detect, + .next = 0 +}; + +GRUB_MOD_INIT(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} + +GRUB_MOD_FINI(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..8b7f5ed 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; - total_sectors = 9000000; /* TODO: get the correct size. */ + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c new file mode 100644 index 0000000..431919a --- /dev/null +++ b/disk/mdraid_linux.c @@ -0,0 +1,233 @@ +/* mdraid_linux.c - module to handle linux softraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define RESERVED_BYTES (64 * 1024) +#define RESERVED_SECTORS (RESERVED_BYTES / 512) + +#define NEW_SIZE_SECTORS(x) ((x & ~(RESERVED_SECTORS - 1)) \ + - RESERVED_SECTORS) + +#define SB_BYTES 4096 +#define SB_WORDS (SB_BYTES / 4) +#define SB_SECTORS (SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define SB_GENERIC_OFFSET 0 + +#define SB_PERSONALITY_OFFSET 64 +#define SB_DISKS_OFFSET 128 +#define SB_DESCRIPTOR_OFFSET 992 + +#define SB_GENERIC_CONSTANT_WORDS 32 +#define SB_GENERIC_STATE_WORDS 32 +#define SB_GENERIC_WORDS (SB_GENERIC_CONSTANT_WORDS + \ + SB_GENERIC_STATE_WORDS) + +#define SB_PERSONALITY_WORDS 64 +#define SB_DESCRIPTOR_WORDS 32 +#define SB_DISKS 27 +#define SB_DISKS_WORDS (SB_DISKS * SB_DESCRIPTOR_WORDS) + +#define SB_RESERVED_WORDS (1024 \ + - SB_GENERIC_WORDS \ + - SB_PERSONALITY_WORDS \ + - SB_DISKS_WORDS \ + - SB_DESCRIPTOR_WORDS) + +#define SB_EQUAL_WORDS (SB_GENERIC_WORDS \ + + SB_PERSONALITY_WORDS \ + + SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define DISK_FAULTY 0 +#define DISK_ACTIVE 1 +#define DISK_SYNC 2 +#define DISK_REMOVED 3 + +#define DISK_WRITEMOSTLY 9 + +#define SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define SB_CLEAN 0 +#define SB_ERRORS 1 + +#define SB_BITMAP_PRESENT 8 + +struct grub_raid_disk_09 +{ + grub_uint32_t number; /* Device number in the entire set. */ + grub_uint32_t major; /* Device major number. */ + grub_uint32_t minor; /* Device minor number. */ + grub_uint32_t raid_disk; /* The role of the device in the raid set. */ + grub_uint32_t state; /* Operational state. */ + grub_uint32_t reserved[SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 +{ + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* MD identifier. */ + grub_uint32_t major_version; /* Major version. */ + grub_uint32_t minor_version; /* Minor version. */ + grub_uint32_t patch_version; /* Patchlevel version. */ + grub_uint32_t gvalid_words; /* Number of used words in this section. */ + grub_uint32_t set_uuid0; /* Raid set identifier. */ + grub_uint32_t ctime; /* Creation time. */ + grub_uint32_t level; /* Raid personality. */ + grub_uint32_t size; /* Apparent size of each individual disk. */ + grub_uint32_t nr_disks; /* Total disks in the raid set. */ + grub_uint32_t raid_disks; /* Disks in a fully functional raid set. */ + grub_uint32_t md_minor; /* Preferred MD minor device number. */ + grub_uint32_t not_persistent; /* Does it have a persistent superblock. */ + grub_uint32_t set_uuid1; /* Raid set identifier #2. */ + grub_uint32_t set_uuid2; /* Raid set identifier #3. */ + grub_uint32_t set_uuid3; /* Raid set identifier #4. */ + grub_uint32_t gstate_creserved[SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* Superblock update time. */ + grub_uint32_t state; /* State bits (clean, ...). */ + grub_uint32_t active_disks; /* Number of currently active disks. */ + grub_uint32_t working_disks; /* Number of working disks. */ + grub_uint32_t failed_disks; /* Number of failed disks. */ + grub_uint32_t spare_disks; /* Number of spare disks. */ + grub_uint32_t sb_csum; /* Checksum of the whole superblock. */ + grub_uint64_t events; /* Superblock update count. */ + grub_uint64_t cp_events; /* Checkpoint update count. */ + grub_uint32_t recovery_cp; /* Recovery checkpoint sector count. */ + grub_uint32_t gstate_sreserved[SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* The array's physical layout. */ + grub_uint32_t chunk_size; /* Chunk size in bytes. */ + grub_uint32_t root_pv; /* LV root PV. */ + grub_uint32_t root_block; /* LV root block. */ + grub_uint32_t pstate_reserved[SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +} __attribute__ ((packed)); + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = NEW_SIZE_SECTORS (size); + + if (grub_disk_read (disk, sector, 0, SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != SB_MAGIC) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + if (sb.level != 0 && sb.level != 1 && sb.level != 4 && + sb.level != 5 && sb.level != 6) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.level); + + if (sb.level >= 5 && sb.layout > GRUB_RAID_LAYOUT_RIGHT_SYMMETRIC) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID5/6 layout: %d", sb.layout); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = sb.layout; + array->total_devs = sb.nr_disks; + array->disk_size = (sb.size) ? sb.size * 2 : sector; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (!array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = { + .name = "mdraid", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT (mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} + +GRUB_MOD_FINI (mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 163bd81..d0dd456 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -43,10 +43,15 @@ grub_is_array_readable (struct grub_raid_array *array) return 1; break; + case 4: case 5: if (array->nr_devs >= array->total_devs - 1) return 1; break; + + case 6: + if (array->nr_devs >= array->total_devs - 2) + return 1; } return 0; @@ -91,6 +96,8 @@ static grub_err_t grub_raid_open (const char *name, grub_disk_t disk) { struct grub_raid_array *array; + grub_disk_addr_t disk_size; + unsigned i, n; for (array = array_list; array != NULL; array = array->next) { @@ -100,28 +107,35 @@ grub_raid_open (const char *name, grub_disk_t disk) } if (!array) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s", name); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s", + name); disk->has_partitions = 1; disk->id = array->number; disk->data = array; + /* Find the smallest disk. */ + disk_size = ULONG_MAX; + for (i = 0; i < array->total_devs; i++) + if ((array->device[i]) && (array->device[i]->total_sectors < disk_size)) + disk_size = array->device[i]->total_sectors; + grub_dprintf ("raid", "%s: total_devs=%d, disk_size=%lld\n", name, - array->total_devs, (unsigned long long) array->disk_size); + array->total_devs, (unsigned long long) disk_size); switch (array->level) { - case 0: - /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; - break; - case 1: - disk->total_sectors = array->disk_size; + disk->total_sectors = disk_size; break; + case 0: + case 4: case 5: - disk->total_sectors = (array->total_devs - 1) * array->disk_size; + case 6: + n = array->level / 3; + + disk->total_sectors = (array->total_devs - n) * disk_size; break; } @@ -137,75 +151,272 @@ grub_raid_close (grub_disk_t disk __attribute ((unused))) return; } +static void +block_xor (char *buf1, char *buf2, int size) +{ + grub_size_t *p1, *p2; + + p1 = (grub_size_t *) buf1; + p2 = (grub_size_t *) buf2; + size /= GRUB_CPU_SIZEOF_VOID_P; + + while (size) + { + *(p1++) ^= *(p2++); + size--; + } +} + static grub_err_t -grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, - grub_size_t size, char *buf) +grub_raid5_recover (struct grub_raid_array *array, int disknr, + char *buf, grub_disk_addr_t sector, int size) { - struct grub_raid_array *array = disk->data; - grub_err_t err = 0; + char *buf2; + int i; - switch (array->level) + size <<= GRUB_DISK_SECTOR_BITS; + buf2 = grub_malloc (size); + if (!buf2) + return grub_errno; + + grub_memset (buf, 0, size); + + for (i = 0; i < (int) array->total_devs; i++) { - case 0: - { - grub_uint64_t a; - grub_uint32_t b; - unsigned int disknr; - grub_disk_addr_t read_sector; - grub_size_t read_size; + grub_err_t err; - /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, array->total_devs, &disknr); + if (i == disknr) + continue; - a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; + err = grub_disk_read (array->device[i], sector, 0, size, buf2); - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; + if (err) + { + grub_free (buf2); + return err; + } - if (read_size > size) - read_size = size; + block_xor (buf, buf2, size); + } - while (1) - { - grub_uint32_t i; + grub_free (buf2); - err = grub_disk_read (array->device[disknr], read_sector, 0, - read_size << GRUB_DISK_SECTOR_BITS, buf); - if (err) - break; + return GRUB_ERR_NONE; +} - buf += read_size << GRUB_DISK_SECTOR_BITS; - size -= read_size; - if (! size) - break; +static grub_uint8_t raid6_table1[256][256]; +static grub_uint8_t raid6_table2[256][256]; +static int raid6_inited; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; +static void +block_mul (grub_uint8_t mul, char *buf, int size) +{ + int i; + grub_uint8_t *p; - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; + p = buf; + for (i = 0; i < size; i++, p++) + *p = raid6_table1[mul][*p]; +} - disknr++; - /* See whether the disk was the last disk, and start - reading from the first disk in that case. */ - if (disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - } - } +static void +grub_raid6_init_table (void) +{ + int i, j; + + for (i = 0; i < 256; i++) + raid6_table1[i][1] = raid6_table1[1][i] = i; + + for (i = 2; i < 256; i++) + for (j = i; j < 256; j++) + { + int n; + grub_uint8_t c; + + n = i >> 1; + + c = raid6_table1[n][j]; + c = (c << 1) ^ ((c & 0x80) ? 0x1d : 0); + if (i & 1) + c ^= j; + + raid6_table1[j][i] = raid6_table1[i][j] = c; + } + + raid6_table2[0][0] = 1; + for (i = 1; i < 256; i++) + raid6_table2[i][i] = raid6_table1[raid6_table2[i - 1][i - 1]][2]; + + for (i = 0; i < 254; i++) + for (j = i + 1; j < 255; j++) + { + grub_uint8_t n, c; + int k; + + n = raid6_table2[j - i][j - i] ^ 1; + c = n; + for (k = 0; k < 253; k++) + c = raid6_table1[c][n]; + + raid6_table2[j][i] = c; + + n = raid6_table2[255 + i - j][255 + i - j] ^ 1; + c = n; + for (k = 0; k < 253; k++) + c = raid6_table1[c][n]; + + raid6_table2[i][j] = c; } - break; + raid6_inited = 1; +} + +static grub_err_t +grub_raid6_recover (struct grub_raid_array *array, int disknr, int p, + char *buf, grub_disk_addr_t sector, int size) +{ + int i, q, pos; + int err[2], nerr; + char *pbuf = 0, *qbuf = 0; + + size <<= GRUB_DISK_SECTOR_BITS; + pbuf = grub_malloc (size); + if (!pbuf) + goto quit; + + qbuf = grub_malloc (size); + if (!qbuf) + goto quit; + + q = p + 1; + if (q == (int) array->total_devs) + q = 0; + + grub_memset (pbuf, 0, size); + grub_memset (qbuf, 0, size); + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + pos = q + 1; + if (pos == (int) array->total_devs) + pos = 0; + } + else + { + pos = 0; + + if (pos == p) + pos += 2; + else if (pos == q) + pos++; + } + + nerr = 1; + for (i = 0; i < (int) array->total_devs - 2; i++) + { + if (pos == disknr) + err[0] = i; + else + { + if ((array->device[pos]) && + (! grub_disk_read (array->device[pos], sector, 0, size, buf))) + { + block_xor (pbuf, buf, size); + block_mul (raid6_table2[i][i], buf, size); + block_xor (qbuf, buf, size); + } + else + { + if (nerr >= 2) + goto quit; + + err[nerr++] = i; + grub_errno = GRUB_ERR_NONE; + } + } + + pos++; + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + if (pos == (int) array->total_devs) + pos = 0; + } + else + { + if (pos == p) + pos += 2; + } + } + + if (nerr == 1) + { + if ((array->device[p]) && + (! grub_disk_read (array->device[p], sector, 0, size, buf))) + { + block_xor (buf, pbuf, size); + goto quit; + } + + if (! array->device[q]) + { + grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore"); + goto quit; + } + + grub_errno = GRUB_ERR_NONE; + if (grub_disk_read (array->device[q], sector, 0, size, buf)) + goto quit; + + block_xor (buf, qbuf, size); + block_mul (raid6_table2[255 - err[0]][255 - err[0]], buf, size); + } + else + { + grub_uint8_t c; + + if ((! array->device[p]) || (! array->device[q])) + { + grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore"); + goto quit; + } + + if (grub_disk_read (array->device[p], sector, 0, size, buf)) + goto quit; + + block_xor (pbuf, buf, size); + + if (grub_disk_read (array->device[q], sector, 0, size, buf)) + goto quit; + + block_xor (qbuf, buf, size); + + c = raid6_table2[err[1]][err[0]]; + c = raid6_table1[raid6_table2[255 - err[0]][255 - err[0]]][c]; + block_mul (c, qbuf, size); + + c = raid6_table1[raid6_table2[err[1]][err[1]]][c]; + block_mul (c, pbuf, size); + + block_xor (pbuf, qbuf, size); + grub_memcpy (buf, pbuf, size); + } + +quit: + grub_free (pbuf); + grub_free (qbuf); + + return grub_errno; +} + +static grub_err_t +grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, + grub_size_t size, char *buf) +{ + struct grub_raid_array *array = disk->data; + grub_err_t err = 0; + + switch (array->level) + { case 1: /* This is easy, we can read from any disk we want. We will loop over all disks until we've found one that is available. In @@ -217,6 +428,10 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, { if (array->device[i]) { + /* Reset read error. */ + if (grub_errno == GRUB_ERR_READ_ERROR) + grub_errno = GRUB_ERR_NONE; + err = grub_disk_read (array->device[i], sector, 0, size << GRUB_DISK_SECTOR_BITS, buf); @@ -227,110 +442,137 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, } break; + case 0: + case 4: case 5: + case 6: { - grub_uint64_t a; - grub_uint32_t b; - int disknr; grub_disk_addr_t read_sector; - grub_size_t read_size; + grub_uint32_t b, p, n, disknr, e; - /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, (array->total_devs - 1), &b); - disknr = b; + /* n = 0 for level 0, 1 for level 4 and 5, 2 for level 6. */ + n = array->level / 3; - a = grub_divmod64 (sector, array->chunk_size * (array->total_devs - 1), - NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; - - grub_divmod64 (read_sector, array->chunk_size * array->total_devs, &b); - disknr -= (b / array->chunk_size); - if (disknr < 0) - disknr += array->total_devs; - - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; + /* Find the first sector to read. */ + read_sector = grub_divmod64 (sector, array->chunk_size, &b); + read_sector = grub_divmod64 (read_sector, array->total_devs - n, + &disknr); + if (array->level >= 5) + { + grub_divmod64 (read_sector, array->total_devs, &p); + + if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)) + p = array->total_devs - 1 - p; + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr += p + n; + if (disknr >= array->total_devs) + disknr -= array->total_devs; + } + else + { + if (disknr >= p) + disknr += n; + } + } + else + p = array->total_devs - n; + + read_sector *= array->chunk_size; while (1) { - grub_uint32_t i; + grub_size_t read_size; + int next_level; + + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + e = 0; + if (array->device[disknr]) + { + /* Reset read error. */ + if (grub_errno == GRUB_ERR_READ_ERROR) + grub_errno = GRUB_ERR_NONE; + + err = grub_disk_read (array->device[disknr], + read_sector + b, 0, + read_size << GRUB_DISK_SECTOR_BITS, + buf); + + if ((err) && (err != GRUB_ERR_READ_ERROR)) + break; + e++; + } + else + err = GRUB_ERR_READ_ERROR; - if (array->device[disknr]) - err = grub_disk_read (array->device[disknr], read_sector, 0, - read_size << GRUB_DISK_SECTOR_BITS, buf); + if (err) + { + if (array->nr_devs < array->total_devs - n + e) + break; - /* If an error occurs when we already have an degraded - array we can't recover from that. */ - if (err && ((array->total_devs - 1) == array->nr_devs)) - break; + grub_errno = GRUB_ERR_NONE; + err = ((array->level == 6) ? + grub_raid6_recover (array, disknr, p, + buf, read_sector + b, read_size) : + grub_raid5_recover (array, disknr, + buf, read_sector + b, read_size)); - if (err || ! array->device[disknr]) - { - /* Either an error occured or the disk is not - available. We have to compute this block from the - blocks on the other hard disks. */ - grub_size_t buf_size = read_size << GRUB_DISK_SECTOR_BITS; - char buf2[buf_size]; - unsigned int j; - - grub_memset (buf, 0, buf_size); - - for (j = 0; j < array->total_devs; j++) - { - unsigned int k; - - if (j != (unsigned int) disknr) - { - err = grub_disk_read (array->device[j], read_sector, - 0, buf_size, buf2); - if (err) - return err; - - for (k = 0; k < buf_size; k++) - buf[k] = buf[k] ^ buf2[k]; - } - } - } + if (err) + break; + } - buf += (read_size << GRUB_DISK_SECTOR_BITS); + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - + b = 0; disknr++; - grub_divmod64 (read_sector, - array->chunk_size * array->total_devs, &i); - if ((unsigned int) disknr == (array->total_devs - (i / array->chunk_size) - 1)) - disknr++; - /* See whether the disk was the last disk, and start - reading from the first disk in that case. */ - if ((unsigned int) disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - grub_divmod64 (read_sector, - array->chunk_size * array->total_devs, &i); - if ((i / array->chunk_size) == (array->total_devs - 1)) - disknr++; - } + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + if (disknr == array->total_devs) + disknr = 0; + + next_level = (disknr == p); + } + else + { + if (disknr == p) + disknr += n; + + next_level = (disknr >= array->total_devs); + } + + if (next_level) + { + read_sector += array->chunk_size; + + if (array->level >= 5) + { + if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK) + p = (p == array->total_devs - 1) ? 0 : p + 1; + else + p = (p == 0) ? array->total_devs - 1 : p - 1; + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr += (n - 1); + if (disknr >= array->total_devs) + disknr -= array->total_devs; + } + else + { + disknr -= array->total_devs; + if (disknr == p) + disknr += n; + } + } + } } } break; @@ -348,169 +590,103 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *scanner_name) { - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); - - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } - - /* FIXME: Check the checksum. */ - - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } - - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } array->name = grub_malloc (13); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } grub_sprintf (array->name, "md%d", array->number); - grub_dprintf ("raid", "Found array: %s\n", array->name); + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, + scanner_name); /* Add our new array to the list. */ array->next = array_list; @@ -518,47 +694,121 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; + + return 0; +} - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) +static grub_raid_t grub_raid_list; + +static void +grub_raid_scan_device (int head_only) +{ + auto int hook (const char *name); + int hook (const char *name) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); + + disk = grub_disk_open (name); + if (!disk) + return 0; + + if (disk->total_sectors == ULONG_MAX) + { + grub_disk_close (disk); + return 0; + } + + for (p = grub_raid_list; p; p = p->next) + { + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + { + if ((array.level == 6) && (! raid6_inited)) + grub_raid6_init_table (); + + return 0; + } + + break; + } + + /* This error usually means it's not raid, no need to display + it. */ + if (grub_errno != GRUB_ERR_OUT_OF_RANGE) + grub_print_error (); + + grub_errno = GRUB_ERR_NONE; + if (head_only) + break; + } + + grub_disk_close (disk); + + return 0; } - if (! array->device[sb.this_disk.number]) + grub_device_iterate (&hook); +} + +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + struct grub_raid_array *p; + int i; - return 0; + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); } - array->nr_devs++; + array_list = 0; +} - return 0; +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; + grub_raid_scan_device (1); +} + +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; + + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} + +void +grub_raid_rescan (void) +{ + free_array (); + grub_raid_scan_device (0); } static struct grub_disk_dev grub_raid_dev = @@ -579,12 +829,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..81eb582 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,51 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + +#define GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC 0 +#define GRUB_RAID_LAYOUT_RIGHT_ASYMMETRIC 1 +#define GRUB_RAID_LAYOUT_LEFT_SYMMETRIC 2 +#define GRUB_RAID_LAYOUT_RIGHT_SYMMETRIC 3 + +#define GRUB_RAID_LAYOUT_RIGHT_MASK 1 +#define GRUB_RAID_LAYOUT_SYMMETRIC_MASK 2 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ - int layout; /* Only for RAID 5. */ + int layout; /* Layout for RAID 5/6. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) - -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) - -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ - -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ - - -#define GRUB_RAID_SB_MAGIC 0xa92b4efc - -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 +struct grub_raid +{ + const char *name; -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; + struct grub_raid *next; }; +typedef struct grub_raid *grub_raid_t; -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +void grub_raid_rescan (void); #endif /* ! GRUB_RAID_H */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..29234ac 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,6 +29,7 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> #include <grub_fstest_init.h> @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -273,32 +273,61 @@ cmd_hex (char *pathname) return 0; } - read_file (pathname, hex_hook); + if (pathname) + read_file (pathname, hex_hook); + else + { + char buf[BUF_SIZE]; + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device"); + + if (! leng) + leng = GRUB_DISK_SECTOR_SIZE; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d\n", + skip, len); + + hexdump (skip, buf, len); + + skip += len; + leng -= len; + } + + grub_device_close (dev); + } } static void -fstest (char *image_path, int cmd, int n, char **args) +fstest (char **images, int num_disks, int cmd, int n, char **args) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; - - - grub_sprintf (host_file, "(host)/%s", image_path); + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails.\n"); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) cmd_cmp (args[0], args[1]); break; case CMD_HEX: - cmd_hex (args[0]); + cmd_hex ((n == 0) ? 0 : args[0]); break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ ls PATH list files in PATH\n\ cp SRC DEST copy file to local system\n\ cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + hex [FILE] Hex dump FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,35 +481,29 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) - { - fprintf (stderr, "No path is specified.\n"); - usage (1); - } - - image_path = argv[optind]; - - if (*image_path != '/') + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "Must use absolute path.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; @@ -478,6 +521,7 @@ main (int argc, char *argv[]) else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +547,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); diff --git a/util/grub-probe.c b/util/grub-probe.c index a4f51e2..ce9cbff 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -142,7 +142,7 @@ probe (const char *path, char *device_name) abstraction_name = "lvm"; break; case GRUB_DEV_ABSTRACTION_RAID: - abstraction_name = "raid"; + abstraction_name = "raid mdraid"; break; default: grub_util_info ("did not find LVM/RAID in %s, assuming raw device", device_name); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-13 20:01 ` Bean @ 2008-08-16 9:07 ` Bean 2008-08-16 12:16 ` Robert Millan 0 siblings, 1 reply; 24+ messages in thread From: Bean @ 2008-08-16 9:07 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 3258 bytes --] Hi, This new patch seperates raid5 and raid6 recover code from raid.c, and place them in module raid5rec.mod and raid6rec.mod. The recover code is only needed when some of the disk are missing or corrupted, which is not common. But raid.c is installed to mbr, so size is important. If there is enough room in mbr for the extra module, they can use --modules option in grub-install to add these modules. 2008-08-16 Bean <bean123ch@gmail.com> * conf/common.rmk (grub_probe_SOURCES): Add disk/mdraid_linux.c. (grub_fstest_SOURCES): Add disk/raid5_recover.c, disk/raid6_recover.c, disk/mdraid_linux.c and disk/dmraid_nvidia.c. (pkglib_MODULES): Add raid5rec.mod, raid6rec.mod, mdraid.mod and dm_nv.mod. (raid5rec_mod_SOURCES): New macro. (raid5rec_mod_CFLAGS): Likewise. (raid5rec_mod_LDFLAGS): Likewise. (raid6rec_mod_SOURCES): Likewise. (raid6rec_mod_CFLAGS): Likewise. (raid6rec_mod_LDFLAGS): Likewise. (mdraid_mod_SOURCES): Likewise. (mdraid_mod_CFLAGS): Likewise. (mdraid_mod_LDFLAGS): Likewise. (dm_nv_mod_SOURCES): Likewise. (dm_nv_mod_CFLAGS): Likewise. (dm_nv_mod_LDFLAGS): Likewise. * conf/i386-pc.rmk (grub_setup_SOURCES): Add disk/mdraid_linux.c. (grub_emu_SOURCES): Add disk/raid5_recover.c, disk/raid6_recover.c, disk/mdraid_linux.c and disk/dmraid_nvidia.c. * conf/i386-coreboot.rmk (grub_emu_SOURCES): Add disk/raid5_recover.c, disk/raid6_recover.c, disk/mdraid_linux.c and disk/dmraid_nvidia.c. * conf/i386-efi.rmk (grub_emu_SOURCES): Likewise. * conf/x86_64-efi.rmk (grub_emu_SOURCES): Likewise. * conf/i386-ieee1275.rmk (grub_emu_SOURCES): Likewise. * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise. * disk/raid5_recover.c: New file. * disk/raid6_recover.c: Likewise. * disk/mdraid_linux.c: Likewise. * disk/dmraid_nvidia.c: Likewise. * disk/i386/pc/biosdisk.c: Set total_sectors of cdrom device to ULONG_MAX. * disk/raid.c (grub_raid_open): Use the size of the smallest disk to calculate the size of raid device. (grub_raid_read): Simplify raid0 code. Support raid4, raid6 and four different layout of raid5. (grub_raid_scan_device): Remove code specific to mdraid. (grub_raid_list): New variable. (free_array): New function. (grub_raid_register): Likewise. (grub_raid_unregister): Likewise. (grub_raid_rescan): Likewise. (GRUB_MOD_INIT): Don't iterate device here. (GRUB_MOD_FINI): Use free_array to release resource. * include/grub/raid.h: Remove macro and structure specific to mdraid. (grub_raid5_recover_func_t): New function variable type. (grub_raid6_recover_func_t): Likewise. (grub_raid5_recover_func): New variable. (grub_raid6_recover_func): Likewise. (grub_raid_register): New function. (grub_raid_unregister): Likewise. (grub_raid_rescan): Likewise. (grub_raid_block_xor): Likewise. * util/grub-fstest.c: Add #include <grub/raid.h>. (part): Removed. (cmd_hex): Handle partition as well. (fstest): Handle multiple disks. (options): Remove part, raw and long, add root and diskcount. (usage): Change hex, remove -p, -r, -l, add -r and -c. (main): Find the first non option entry and ignore subsequence options, add handling for the new options, support multiple disks. * util/grub-probe.c (probe): Add mdraid to abstraction_name. -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_9.diff --] [-- Type: text/x-diff; name=raid_9.diff, Size: 68393 bytes --] diff --git a/conf/common.rmk b/conf/common.rmk index 9ca03d4..2c94966 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -21,7 +21,7 @@ grub_probe_SOURCES = util/grub-probe.c \ \ partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c \ - disk/lvm.c disk/raid.c grub_probe_init.c + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c ifeq ($(enable_grub_fstest), yes) bin_UTILITIES += grub-fstest @@ -40,7 +40,9 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ fs/ufs.c fs/xfs.c fs/afs.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ - kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c # For the parser. @@ -270,14 +272,35 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod raid5rec.mod raid6rec.mod mdraid.mod dm_nv.mod \ + lvm.mod # For raid.mod raid_mod_SOURCES = disk/raid.c raid_mod_CFLAGS = $(COMMON_CFLAGS) raid_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For raid.mod +# For raid5rec.mod +raid5rec_mod_SOURCES = disk/raid5_recover.c +raid5rec_mod_CFLAGS = $(COMMON_CFLAGS) +raid5rec_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For raid6rec.mod +raid6rec_mod_SOURCES = disk/raid6_recover.c +raid6rec_mod_CFLAGS = $(COMMON_CFLAGS) +raid6rec_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For lvm.mod lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk index 34b3b40..f3eac0d 100644 --- a/conf/i386-coreboot.rmk +++ b/conf/i386-coreboot.rmk @@ -85,7 +85,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk index ef7d8a2..0b9611f 100644 --- a/conf/i386-efi.rmk +++ b/conf/i386-efi.rmk @@ -62,7 +62,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index d62a656..6477a6f 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -83,7 +83,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 1ad2e73..1648518 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -102,7 +102,7 @@ grub_setup_SOURCES = util/i386/pc/grub-setup.c util/biosdisk.c \ \ partmap/pc.c partmap/gpt.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/lvm.c \ util/raid.c util/lvm.c \ grub_setup_init.c @@ -142,7 +142,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk index 7e4aaa9..d97207d 100644 --- a/conf/powerpc-ieee1275.rmk +++ b/conf/powerpc-ieee1275.rmk @@ -67,7 +67,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/powerpc/ieee1275/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_script.tab.c grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk index 473c34e..66e9e85 100644 --- a/conf/x86_64-efi.rmk +++ b/conf/x86_64-efi.rmk @@ -64,7 +64,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c new file mode 100644 index 0000000..c2d5ed3 --- /dev/null +++ b/disk/dmraid_nvidia.c @@ -0,0 +1,166 @@ +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +#define NV_SIGNATURES 4 + +#define NV_IDLE 0 +#define NV_SCDB_INIT_RAID 2 +#define NV_SCDB_REBUILD_RAID 3 +#define NV_SCDB_UPGRADE_RAID 4 +#define NV_SCDB_SYNC_RAID 5 + +#define NV_LEVEL_UNKNOWN 0x00 +#define NV_LEVEL_JBOD 0xFF +#define NV_LEVEL_0 0x80 +#define NV_LEVEL_1 0x81 +#define NV_LEVEL_3 0x83 +#define NV_LEVEL_5 0x85 +#define NV_LEVEL_10 0x8a +#define NV_LEVEL_1_0 0x8180 + +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ + +struct grub_nv_array +{ + grub_uint32_t version; + grub_uint32_t signature[NV_SIGNATURES]; + grub_uint8_t raid_job_code; + grub_uint8_t stripe_width; + grub_uint8_t total_volumes; + grub_uint8_t original_width; + grub_uint32_t raid_level; + grub_uint32_t stripe_block_size; + grub_uint32_t stripe_block_size_bytes; + grub_uint32_t stripe_block_size_log2; + grub_uint32_t stripe_mask; + grub_uint32_t stripe_size; + grub_uint32_t stripe_size_bytes; + grub_uint32_t raid_job_mask; + grub_uint32_t original_capacity; + grub_uint32_t flags; +}; + +#define NV_ID_LEN 8 +#define NV_ID_STRING "NVIDIA" +#define NV_VERSION 100 + +#define NV_PRODID_LEN 16 +#define NV_PRODREV_LEN 4 + +struct grub_nv_super +{ + char vendor[NV_ID_LEN]; /* 0x00 - 0x07 ID string. */ + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ + grub_uint8_t reserved; /* 0x13. */ + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ + char prodid[NV_PRODID_LEN]; /* 0x1C - 0x2B Array product ID. */ + char prodrev[NV_PRODREV_LEN]; /* 0x2C - 0x2F Array product revision */ + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ + struct grub_nv_array array; /* Array information */ +} __attribute__ ((packed)); + +static grub_err_t +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + struct grub_nv_super sb; + grub_uint32_t *uuid; + + if (disk->partition) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "skip partition"); + + sector = grub_disk_get_size (disk) - 2; + + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) + return grub_errno; + + if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + if (sb.version != NV_VERSION) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unknown version: %d.%d", sb.version); + + switch (sb.array.raid_level) + { + case NV_LEVEL_0: + array->level = 0; + array->disk_size = sb.capacity / sb.array.total_volumes; + break; + + case NV_LEVEL_1: + array->level = 1; + array->disk_size = sb.capacity; + break; + + case NV_LEVEL_5: + array->level = 5; + array->layout = GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC; + array->disk_size = sb.capacity / (sb.array.total_volumes - 1); + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.array.raid_level); + } + + array->number = 0; + array->total_devs = sb.array.total_volumes; + array->chunk_size = sb.array.stripe_block_size; + array->index = sb.unit_number; + array->uuid_len = sizeof (sb.array.signature); + array->uuid = grub_malloc (sizeof (sb.array.signature)); + if (! array->uuid) + return grub_errno; + + grub_memcpy (array->uuid, (char *) &sb.array.signature, + sizeof (sb.array.signature)); + + return 0; +} + +static struct grub_raid grub_dmraid_nv_dev = +{ + .name = "dmraid_nv", + .detect = grub_dmraid_nv_detect, + .next = 0 +}; + +GRUB_MOD_INIT(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} + +GRUB_MOD_FINI(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..8b7f5ed 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; - total_sectors = 9000000; /* TODO: get the correct size. */ + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c new file mode 100644 index 0000000..a43cb89 --- /dev/null +++ b/disk/mdraid_linux.c @@ -0,0 +1,235 @@ +/* mdraid_linux.c - module to handle linux softraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define RESERVED_BYTES (64 * 1024) +#define RESERVED_SECTORS (RESERVED_BYTES / 512) + +#define NEW_SIZE_SECTORS(x) ((x & ~(RESERVED_SECTORS - 1)) \ + - RESERVED_SECTORS) + +#define SB_BYTES 4096 +#define SB_WORDS (SB_BYTES / 4) +#define SB_SECTORS (SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define SB_GENERIC_OFFSET 0 + +#define SB_PERSONALITY_OFFSET 64 +#define SB_DISKS_OFFSET 128 +#define SB_DESCRIPTOR_OFFSET 992 + +#define SB_GENERIC_CONSTANT_WORDS 32 +#define SB_GENERIC_STATE_WORDS 32 +#define SB_GENERIC_WORDS (SB_GENERIC_CONSTANT_WORDS + \ + SB_GENERIC_STATE_WORDS) + +#define SB_PERSONALITY_WORDS 64 +#define SB_DESCRIPTOR_WORDS 32 +#define SB_DISKS 27 +#define SB_DISKS_WORDS (SB_DISKS * SB_DESCRIPTOR_WORDS) + +#define SB_RESERVED_WORDS (1024 \ + - SB_GENERIC_WORDS \ + - SB_PERSONALITY_WORDS \ + - SB_DISKS_WORDS \ + - SB_DESCRIPTOR_WORDS) + +#define SB_EQUAL_WORDS (SB_GENERIC_WORDS \ + + SB_PERSONALITY_WORDS \ + + SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define DISK_FAULTY 0 +#define DISK_ACTIVE 1 +#define DISK_SYNC 2 +#define DISK_REMOVED 3 + +#define DISK_WRITEMOSTLY 9 + +#define SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define SB_CLEAN 0 +#define SB_ERRORS 1 + +#define SB_BITMAP_PRESENT 8 + +struct grub_raid_disk_09 +{ + grub_uint32_t number; /* Device number in the entire set. */ + grub_uint32_t major; /* Device major number. */ + grub_uint32_t minor; /* Device minor number. */ + grub_uint32_t raid_disk; /* The role of the device in the raid set. */ + grub_uint32_t state; /* Operational state. */ + grub_uint32_t reserved[SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 +{ + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* MD identifier. */ + grub_uint32_t major_version; /* Major version. */ + grub_uint32_t minor_version; /* Minor version. */ + grub_uint32_t patch_version; /* Patchlevel version. */ + grub_uint32_t gvalid_words; /* Number of used words in this section. */ + grub_uint32_t set_uuid0; /* Raid set identifier. */ + grub_uint32_t ctime; /* Creation time. */ + grub_uint32_t level; /* Raid personality. */ + grub_uint32_t size; /* Apparent size of each individual disk. */ + grub_uint32_t nr_disks; /* Total disks in the raid set. */ + grub_uint32_t raid_disks; /* Disks in a fully functional raid set. */ + grub_uint32_t md_minor; /* Preferred MD minor device number. */ + grub_uint32_t not_persistent; /* Does it have a persistent superblock. */ + grub_uint32_t set_uuid1; /* Raid set identifier #2. */ + grub_uint32_t set_uuid2; /* Raid set identifier #3. */ + grub_uint32_t set_uuid3; /* Raid set identifier #4. */ + grub_uint32_t gstate_creserved[SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* Superblock update time. */ + grub_uint32_t state; /* State bits (clean, ...). */ + grub_uint32_t active_disks; /* Number of currently active disks. */ + grub_uint32_t working_disks; /* Number of working disks. */ + grub_uint32_t failed_disks; /* Number of failed disks. */ + grub_uint32_t spare_disks; /* Number of spare disks. */ + grub_uint32_t sb_csum; /* Checksum of the whole superblock. */ + grub_uint64_t events; /* Superblock update count. */ + grub_uint64_t cp_events; /* Checkpoint update count. */ + grub_uint32_t recovery_cp; /* Recovery checkpoint sector count. */ + grub_uint32_t gstate_sreserved[SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* The array's physical layout. */ + grub_uint32_t chunk_size; /* Chunk size in bytes. */ + grub_uint32_t root_pv; /* LV root PV. */ + grub_uint32_t root_block; /* LV root block. */ + grub_uint32_t pstate_reserved[SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +} __attribute__ ((packed)); + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = NEW_SIZE_SECTORS (size); + + if (grub_disk_read (disk, sector, 0, SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != SB_MAGIC) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + if (sb.level != 0 && sb.level != 1 && sb.level != 4 && + sb.level != 5 && sb.level != 6) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.level); + + if (sb.level >= 5 && sb.layout > GRUB_RAID_LAYOUT_RIGHT_SYMMETRIC) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID5/6 layout: %d", sb.layout); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = sb.layout; + array->layout = ((sb.level == 6) ? GRUB_RAID_LAYOUT_LEFT_SYMMETRIC : + (sb.level == 5) ? sb.layout : 0); + array->total_devs = sb.nr_disks; + array->disk_size = (sb.size) ? sb.size * 2 : sector; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (!array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = { + .name = "mdraid", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT (mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} + +GRUB_MOD_FINI (mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 163bd81..2c1301c 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -26,6 +26,8 @@ /* Linked list of RAID arrays. */ static struct grub_raid_array *array_list; +grub_raid5_recover_func_t grub_raid5_recover_func; +grub_raid6_recover_func_t grub_raid6_recover_func; \f static char @@ -43,10 +45,15 @@ grub_is_array_readable (struct grub_raid_array *array) return 1; break; + case 4: case 5: if (array->nr_devs >= array->total_devs - 1) return 1; break; + + case 6: + if (array->nr_devs >= array->total_devs - 2) + return 1; } return 0; @@ -91,6 +98,8 @@ static grub_err_t grub_raid_open (const char *name, grub_disk_t disk) { struct grub_raid_array *array; + grub_disk_addr_t disk_size; + unsigned i, n; for (array = array_list; array != NULL; array = array->next) { @@ -100,28 +109,35 @@ grub_raid_open (const char *name, grub_disk_t disk) } if (!array) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s", name); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s", + name); disk->has_partitions = 1; disk->id = array->number; disk->data = array; + /* Find the smallest disk. */ + disk_size = ULONG_MAX; + for (i = 0; i < array->total_devs; i++) + if ((array->device[i]) && (array->device[i]->total_sectors < disk_size)) + disk_size = array->device[i]->total_sectors; + grub_dprintf ("raid", "%s: total_devs=%d, disk_size=%lld\n", name, - array->total_devs, (unsigned long long) array->disk_size); + array->total_devs, (unsigned long long) disk_size); switch (array->level) { - case 0: - /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; - break; - case 1: - disk->total_sectors = array->disk_size; + disk->total_sectors = disk_size; break; + case 0: + case 4: case 5: - disk->total_sectors = (array->total_devs - 1) * array->disk_size; + case 6: + n = array->level / 3; + + disk->total_sectors = (array->total_devs - n) * disk_size; break; } @@ -137,6 +153,22 @@ grub_raid_close (grub_disk_t disk __attribute ((unused))) return; } +void +grub_raid_block_xor (char *buf1, char *buf2, int size) +{ + grub_size_t *p1, *p2; + + p1 = (grub_size_t *) buf1; + p2 = (grub_size_t *) buf2; + size /= GRUB_CPU_SIZEOF_VOID_P; + + while (size) + { + *(p1++) ^= *(p2++); + size--; + } +} + static grub_err_t grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) @@ -146,66 +178,6 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, switch (array->level) { - case 0: - { - grub_uint64_t a; - grub_uint32_t b; - unsigned int disknr; - grub_disk_addr_t read_sector; - grub_size_t read_size; - - /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, array->total_devs, &disknr); - - a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; - - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; - - while (1) - { - grub_uint32_t i; - - err = grub_disk_read (array->device[disknr], read_sector, 0, - read_size << GRUB_DISK_SECTOR_BITS, buf); - if (err) - break; - - buf += read_size << GRUB_DISK_SECTOR_BITS; - size -= read_size; - if (! size) - break; - - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - - disknr++; - /* See whether the disk was the last disk, and start - reading from the first disk in that case. */ - if (disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - } - } - } - break; - case 1: /* This is easy, we can read from any disk we want. We will loop over all disks until we've found one that is available. In @@ -217,6 +189,10 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, { if (array->device[i]) { + /* Reset read error. */ + if (grub_errno == GRUB_ERR_READ_ERROR) + grub_errno = GRUB_ERR_NONE; + err = grub_disk_read (array->device[i], sector, 0, size << GRUB_DISK_SECTOR_BITS, buf); @@ -227,110 +203,154 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, } break; + case 0: + case 4: case 5: + case 6: { - grub_uint64_t a; - grub_uint32_t b; - int disknr; grub_disk_addr_t read_sector; - grub_size_t read_size; - - /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, (array->total_devs - 1), &b); - disknr = b; - - a = grub_divmod64 (sector, array->chunk_size * (array->total_devs - 1), - NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; + grub_uint32_t b, p, n, disknr, e; - grub_divmod64 (read_sector, array->chunk_size * array->total_devs, &b); - disknr -= (b / array->chunk_size); - if (disknr < 0) - disknr += array->total_devs; + /* n = 0 for level 0, 1 for level 4 and 5, 2 for level 6. */ + n = array->level / 3; - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; + /* Find the first sector to read. */ + read_sector = grub_divmod64 (sector, array->chunk_size, &b); + read_sector = grub_divmod64 (read_sector, array->total_devs - n, + &disknr); + //grub_printf ("T: %d\n", array->chunk_size); + //grub_printf ("A: %d %llx %d %d\n", disknr, read_sector, size, b); + if (array->level >= 5) + { + grub_divmod64 (read_sector, array->total_devs, &p); + + if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)) + p = array->total_devs - 1 - p; + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr += p + n; + if (disknr >= array->total_devs) + disknr -= array->total_devs; + } + else + { + if (disknr >= p) + disknr += n; + } + } + else + p = array->total_devs - n; + + read_sector *= array->chunk_size; while (1) { - grub_uint32_t i; - - if (array->device[disknr]) - err = grub_disk_read (array->device[disknr], read_sector, 0, - read_size << GRUB_DISK_SECTOR_BITS, buf); - - /* If an error occurs when we already have an degraded - array we can't recover from that. */ - if (err && ((array->total_devs - 1) == array->nr_devs)) - break; + grub_size_t read_size; + int next_level; + + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + e = 0; + if (array->device[disknr]) + { + /* Reset read error. */ + if (grub_errno == GRUB_ERR_READ_ERROR) + grub_errno = GRUB_ERR_NONE; + + err = grub_disk_read (array->device[disknr], + read_sector + b, 0, + read_size << GRUB_DISK_SECTOR_BITS, + buf); + + if ((err) && (err != GRUB_ERR_READ_ERROR)) + break; + e++; + } + else + err = GRUB_ERR_READ_ERROR; - if (err || ! array->device[disknr]) - { - /* Either an error occured or the disk is not - available. We have to compute this block from the - blocks on the other hard disks. */ - grub_size_t buf_size = read_size << GRUB_DISK_SECTOR_BITS; - char buf2[buf_size]; - unsigned int j; - - grub_memset (buf, 0, buf_size); - - for (j = 0; j < array->total_devs; j++) - { - unsigned int k; - - if (j != (unsigned int) disknr) - { - err = grub_disk_read (array->device[j], read_sector, - 0, buf_size, buf2); - if (err) - return err; - - for (k = 0; k < buf_size; k++) - buf[k] = buf[k] ^ buf2[k]; - } - } - } + if (err) + { + if (array->nr_devs < array->total_devs - n + e) + break; + + grub_errno = GRUB_ERR_NONE; + if (array->level == 6) + { + err = ((grub_raid6_recover_func) ? + (*grub_raid6_recover_func) (array, disknr, p, + buf, read_sector + b, + read_size) : + grub_error (GRUB_ERR_BAD_DEVICE, + "raid6rec is not loaded")); + } + else + { + err = ((grub_raid5_recover_func) ? + (*grub_raid5_recover_func) (array, disknr, + buf, read_sector + b, + read_size) : + grub_error (GRUB_ERR_BAD_DEVICE, + "raid5rec is not loaded")); + } + + if (err) + break; + } - buf += (read_size << GRUB_DISK_SECTOR_BITS); + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - + b = 0; disknr++; - grub_divmod64 (read_sector, - array->chunk_size * array->total_devs, &i); - if ((unsigned int) disknr == (array->total_devs - (i / array->chunk_size) - 1)) - disknr++; - /* See whether the disk was the last disk, and start - reading from the first disk in that case. */ - if ((unsigned int) disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - grub_divmod64 (read_sector, - array->chunk_size * array->total_devs, &i); - if ((i / array->chunk_size) == (array->total_devs - 1)) - disknr++; - } + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + if (disknr == array->total_devs) + disknr = 0; + + next_level = (disknr == p); + } + else + { + if (disknr == p) + disknr += n; + + next_level = (disknr >= array->total_devs); + } + + if (next_level) + { + read_sector += array->chunk_size; + + if (array->level >= 5) + { + if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK) + p = (p == array->total_devs - 1) ? 0 : p + 1; + else + p = (p == 0) ? array->total_devs - 1 : p - 1; + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr = p + n; + if (disknr >= array->total_devs) + disknr -= array->total_devs; + } + else + { + disknr -= array->total_devs; + if (disknr == p) + disknr += n; + } + } + else + disknr = 0; + } } } break; @@ -348,169 +368,103 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *scanner_name) { - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); - - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } - - /* FIXME: Check the checksum. */ - - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } - - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } array->name = grub_malloc (13); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } grub_sprintf (array->name, "md%d", array->number); - grub_dprintf ("raid", "Found array: %s\n", array->name); + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, + scanner_name); /* Add our new array to the list. */ array->next = array_list; @@ -518,47 +472,116 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; + + return 0; +} - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) +static grub_raid_t grub_raid_list; + +static void +grub_raid_scan_device (int head_only) +{ + auto int hook (const char *name); + int hook (const char *name) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); + + disk = grub_disk_open (name); + if (!disk) + return 0; + + if (disk->total_sectors == ULONG_MAX) + { + grub_disk_close (disk); + return 0; + } + + for (p = grub_raid_list; p; p = p->next) + { + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + return 0; + + break; + } + + /* This error usually means it's not raid, no need to display + it. */ + if (grub_errno != GRUB_ERR_OUT_OF_RANGE) + grub_print_error (); + + grub_errno = GRUB_ERR_NONE; + if (head_only) + break; + } + + grub_disk_close (disk); + + return 0; } - if (! array->device[sb.this_disk.number]) + grub_device_iterate (&hook); +} + +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + struct grub_raid_array *p; + int i; - return 0; + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); } - array->nr_devs++; + array_list = 0; +} - return 0; +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; + grub_raid_scan_device (1); +} + +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; + + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} + +void +grub_raid_rescan (void) +{ + free_array (); + grub_raid_scan_device (0); } static struct grub_disk_dev grub_raid_dev = @@ -579,12 +602,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/disk/raid5_recover.c b/disk/raid5_recover.c new file mode 100755 index 0000000..31cef88 --- /dev/null +++ b/disk/raid5_recover.c @@ -0,0 +1,72 @@ +/* raid5_recover.c - module to recover from faulty RAID4/5 arrays. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +static grub_err_t +grub_raid5_recover (struct grub_raid_array *array, int disknr, + char *buf, grub_disk_addr_t sector, int size) +{ + char *buf2; + int i; + + size <<= GRUB_DISK_SECTOR_BITS; + buf2 = grub_malloc (size); + if (!buf2) + return grub_errno; + + grub_memset (buf, 0, size); + + for (i = 0; i < (int) array->total_devs; i++) + { + grub_err_t err; + + if (i == disknr) + continue; + + err = grub_disk_read (array->device[i], sector, 0, size, buf2); + + if (err) + { + grub_free (buf2); + return err; + } + + grub_raid_block_xor (buf, buf2, size); + } + + grub_free (buf2); + + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT(raid5rec) +{ + grub_raid5_recover_func = grub_raid5_recover; +} + +GRUB_MOD_FINI(raid5rec) +{ + grub_raid5_recover_func = 0; +} diff --git a/disk/raid6_recover.c b/disk/raid6_recover.c new file mode 100755 index 0000000..92543cc --- /dev/null +++ b/disk/raid6_recover.c @@ -0,0 +1,229 @@ +/* raid6_recover.c - module to recover from faulty RAID6 arrays. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +static grub_uint8_t raid6_table1[256][256]; +static grub_uint8_t raid6_table2[256][256]; + +static void +grub_raid_block_mul (grub_uint8_t mul, char *buf, int size) +{ + int i; + grub_uint8_t *p; + + p = buf; + for (i = 0; i < size; i++, p++) + *p = raid6_table1[mul][*p]; +} + +static void +grub_raid6_init_table (void) +{ + int i, j; + + for (i = 0; i < 256; i++) + raid6_table1[i][1] = raid6_table1[1][i] = i; + + for (i = 2; i < 256; i++) + for (j = i; j < 256; j++) + { + int n; + grub_uint8_t c; + + n = i >> 1; + + c = raid6_table1[n][j]; + c = (c << 1) ^ ((c & 0x80) ? 0x1d : 0); + if (i & 1) + c ^= j; + + raid6_table1[j][i] = raid6_table1[i][j] = c; + } + + raid6_table2[0][0] = 1; + for (i = 1; i < 256; i++) + raid6_table2[i][i] = raid6_table1[raid6_table2[i - 1][i - 1]][2]; + + for (i = 0; i < 254; i++) + for (j = 0; j < 254; j++) + { + grub_uint8_t c, n; + int k; + + if (i == j) + continue; + + k = i - j; + if (k < 0) + k += 255; + + c = n = raid6_table2[k][k] ^ 1; + for (k = 0; k < 253; k++) + c = raid6_table1[c][n]; + + raid6_table2[i][j] = raid6_table1[raid6_table2[255 - j][255 - j]][c]; + } +} + +static grub_err_t +grub_raid6_recover (struct grub_raid_array *array, int disknr, int p, + char *buf, grub_disk_addr_t sector, int size) +{ + int i, q, pos; + int err[2], nerr; + char *pbuf = 0, *qbuf = 0; + + size <<= GRUB_DISK_SECTOR_BITS; + pbuf = grub_malloc (size); + if (!pbuf) + goto quit; + + qbuf = grub_malloc (size); + if (!qbuf) + goto quit; + + q = p + 1; + if (q == (int) array->total_devs) + q = 0; + + grub_memset (pbuf, 0, size); + grub_memset (qbuf, 0, size); + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + pos = q + 1; + if (pos == (int) array->total_devs) + pos = 0; + } + else + { + pos = 0; + + if (pos == p) + pos += 2; + else if (pos == q) + pos++; + } + + nerr = 1; + for (i = 0; i < (int) array->total_devs - 2; i++) + { + if (pos == disknr) + err[0] = i; + else + { + if ((array->device[pos]) && + (! grub_disk_read (array->device[pos], sector, 0, size, buf))) + { + grub_raid_block_xor (pbuf, buf, size); + grub_raid_block_mul (raid6_table2[i][i], buf, size); + grub_raid_block_xor (qbuf, buf, size); + } + else + { + if (nerr >= 2) + goto quit; + + err[nerr++] = i; + grub_errno = GRUB_ERR_NONE; + } + } + + pos++; + /* Currently, RAID6 only uses symmetric layout. */ + if (pos == (int) array->total_devs) + pos = 0; + } + + if (nerr == 1) + { + if ((array->device[p]) && + (! grub_disk_read (array->device[p], sector, 0, size, buf))) + { + grub_raid_block_xor (buf, pbuf, size); + goto quit; + } + + if (! array->device[q]) + { + grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore"); + goto quit; + } + + grub_errno = GRUB_ERR_NONE; + if (grub_disk_read (array->device[q], sector, 0, size, buf)) + goto quit; + + grub_raid_block_xor (buf, qbuf, size); + grub_raid_block_mul (raid6_table2[255 - err[0]][255 - err[0]], buf, + size); + } + else + { + grub_uint8_t c; + + if ((! array->device[p]) || (! array->device[q])) + { + grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore"); + goto quit; + } + + if (grub_disk_read (array->device[p], sector, 0, size, buf)) + goto quit; + + grub_raid_block_xor (pbuf, buf, size); + + if (grub_disk_read (array->device[q], sector, 0, size, buf)) + goto quit; + + grub_raid_block_xor (qbuf, buf, size); + + c = raid6_table2[err[1]][err[0]]; + grub_raid_block_mul (c, qbuf, size); + + c = raid6_table1[raid6_table2[err[1]][err[1]]][c]; + grub_raid_block_mul (c, pbuf, size); + + grub_raid_block_xor (pbuf, qbuf, size); + grub_memcpy (buf, pbuf, size); + } + +quit: + grub_free (pbuf); + grub_free (qbuf); + + return grub_errno; +} + +GRUB_MOD_INIT(raid6rec) +{ + grub_raid6_init_table (); + grub_raid6_recover_func = grub_raid6_recover; +} + +GRUB_MOD_FINI(raid6rec) +{ + grub_raid6_recover_func = 0; +} diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..a36be6d 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,65 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + +#define GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC 0 +#define GRUB_RAID_LAYOUT_RIGHT_ASYMMETRIC 1 +#define GRUB_RAID_LAYOUT_LEFT_SYMMETRIC 2 +#define GRUB_RAID_LAYOUT_RIGHT_SYMMETRIC 3 + +#define GRUB_RAID_LAYOUT_RIGHT_MASK 1 +#define GRUB_RAID_LAYOUT_SYMMETRIC_MASK 2 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ - int layout; /* Only for RAID 5. */ + int layout; /* Layout for RAID 5/6. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) +struct grub_raid +{ + const char *name; -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + struct grub_raid *next; +}; +typedef struct grub_raid *grub_raid_t; -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); +void grub_raid_rescan (void); +void grub_raid_block_xor (char *buf1, char *buf2, int size); -#define GRUB_RAID_SB_MAGIC 0xa92b4efc +typedef grub_err_t (*grub_raid5_recover_func_t) (struct grub_raid_array *array, + int disknr, char *buf, + grub_disk_addr_t sector, + int size); -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 - -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ - -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; -}; +typedef grub_err_t (*grub_raid6_recover_func_t) (struct grub_raid_array *array, + int disknr, int p, char *buf, + grub_disk_addr_t sector, + int size); -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; - - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +extern grub_raid5_recover_func_t grub_raid5_recover_func; +extern grub_raid6_recover_func_t grub_raid6_recover_func; #endif /* ! GRUB_RAID_H */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..29234ac 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,6 +29,7 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> #include <grub_fstest_init.h> @@ -141,7 +142,6 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -273,32 +273,61 @@ cmd_hex (char *pathname) return 0; } - read_file (pathname, hex_hook); + if (pathname) + read_file (pathname, hex_hook); + else + { + char buf[BUF_SIZE]; + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device"); + + if (! leng) + leng = GRUB_DISK_SECTOR_SIZE; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d\n", + skip, len); + + hexdump (skip, buf, len); + + skip += len; + leng -= len; + } + + grub_device_close (dev); + } } static void -fstest (char *image_path, int cmd, int n, char **args) +fstest (char **images, int num_disks, int cmd, int n, char **args) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; - - - grub_sprintf (host_file, "(host)/%s", image_path); + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails.\n"); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -311,31 +340,31 @@ fstest (char *image_path, int cmd, int n, char **args) cmd_cmp (args[0], args[1]); break; case CMD_HEX: - cmd_hex (args[0]); + cmd_hex ((n == 0) ? 0 : args[0]); break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -353,15 +382,14 @@ Debug tool for filesystem driver.\n\ ls PATH list files in PATH\n\ cp SRC DEST copy file to local system\n\ cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + hex [FILE] Hex dump FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +402,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,35 +481,29 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) - { - fprintf (stderr, "No path is specified.\n"); - usage (1); - } - - image_path = argv[optind]; - - if (*image_path != '/') + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "Must use absolute path.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; @@ -478,6 +521,7 @@ main (int argc, char *argv[]) else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +547,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); diff --git a/util/grub-probe.c b/util/grub-probe.c index a4f51e2..ce9cbff 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -142,7 +142,7 @@ probe (const char *path, char *device_name) abstraction_name = "lvm"; break; case GRUB_DEV_ABSTRACTION_RAID: - abstraction_name = "raid"; + abstraction_name = "raid mdraid"; break; default: grub_util_info ("did not find LVM/RAID in %s, assuming raw device", device_name); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-16 9:07 ` Bean @ 2008-08-16 12:16 ` Robert Millan 2008-08-16 15:20 ` Bean 0 siblings, 1 reply; 24+ messages in thread From: Robert Millan @ 2008-08-16 12:16 UTC (permalink / raw) To: The development of GRUB 2 On Sat, Aug 16, 2008 at 05:07:00PM +0800, Bean wrote: > Hi, > > This new patch seperates raid5 and raid6 recover code from raid.c, and > place them in module raid5rec.mod and raid6rec.mod. The recover code > is only needed when some of the disk are missing or corrupted, which > is not common. But raid.c is installed to mbr, so size is important. > If there is enough room in mbr for the extra module, they can use > --modules option in grub-install to add these modules. I find this scary in the sense that users need to know about these modules to get the benefit of recovery, and even then, they also need to know they are affected by this problem when they install GRUB (since otherwise they won't be able to bootstrap). Is it possible to detect whether recover code will be needed when grub-install is run, and then either add the extra modules or abort with an error? -- Robert Millan The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and how) you may access your data; but nobody's threatening your freedom: we still allow you to remove your data and not access it at all." ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-16 12:16 ` Robert Millan @ 2008-08-16 15:20 ` Bean 2008-08-17 14:19 ` Bean 2008-08-17 14:52 ` Robert Millan 0 siblings, 2 replies; 24+ messages in thread From: Bean @ 2008-08-16 15:20 UTC (permalink / raw) To: The development of GRUB 2 On Sat, Aug 16, 2008 at 8:16 PM, Robert Millan <rmh@aybabtu.com> wrote: > On Sat, Aug 16, 2008 at 05:07:00PM +0800, Bean wrote: >> Hi, >> >> This new patch seperates raid5 and raid6 recover code from raid.c, and >> place them in module raid5rec.mod and raid6rec.mod. The recover code >> is only needed when some of the disk are missing or corrupted, which >> is not common. But raid.c is installed to mbr, so size is important. >> If there is enough room in mbr for the extra module, they can use >> --modules option in grub-install to add these modules. > > I find this scary in the sense that users need to know about these modules > to get the benefit of recovery, and even then, they also need to know they > are affected by this problem when they install GRUB (since otherwise they > won't be able to bootstrap). > > Is it possible to detect whether recover code will be needed when grub-install > is run, and then either add the extra modules or abort with an error? Hi, grub-probe don't use the recover module, so if it have problem at install time, user would know about it (error message would be "raid5rec not loaded"). Although, if problem occur afterwards, then it's unfortunate. It would be possible to detect them and add it to grub-probe, but the question is, it's not possible to calculate the size of core.img in grub-probe. if we add it statically, it could cause grub-install to fail later. -- Bean ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-16 15:20 ` Bean @ 2008-08-17 14:19 ` Bean 2008-08-17 14:52 ` Robert Millan 1 sibling, 0 replies; 24+ messages in thread From: Bean @ 2008-08-17 14:19 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 1766 bytes --] On Sat, Aug 16, 2008 at 11:20 PM, Bean <bean123ch@gmail.com> wrote: > On Sat, Aug 16, 2008 at 8:16 PM, Robert Millan <rmh@aybabtu.com> wrote: >> On Sat, Aug 16, 2008 at 05:07:00PM +0800, Bean wrote: >>> Hi, >>> >>> This new patch seperates raid5 and raid6 recover code from raid.c, and >>> place them in module raid5rec.mod and raid6rec.mod. The recover code >>> is only needed when some of the disk are missing or corrupted, which >>> is not common. But raid.c is installed to mbr, so size is important. >>> If there is enough room in mbr for the extra module, they can use >>> --modules option in grub-install to add these modules. >> >> I find this scary in the sense that users need to know about these modules >> to get the benefit of recovery, and even then, they also need to know they >> are affected by this problem when they install GRUB (since otherwise they >> won't be able to bootstrap). >> >> Is it possible to detect whether recover code will be needed when grub-install >> is run, and then either add the extra modules or abort with an error? > > Hi, > > grub-probe don't use the recover module, so if it have problem at > install time, user would know about it (error message would be > "raid5rec not loaded"). Although, if problem occur afterwards, then > it's unfortunate. It would be possible to detect them and add it to > grub-probe, but the question is, it's not possible to calculate the > size of core.img in grub-probe. if we add it statically, it could > cause grub-install to fail later. Hi, This patch include the following updates: * add crc command to grub-fstest, also allow to compare device. * support multipath * support raid10, all layouts (near, far, offset) * fix raid6 code when the layout is not left symmetric. -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_10.diff --] [-- Type: text/x-diff; name=raid_10.diff, Size: 75109 bytes --] diff --git a/conf/common.rmk b/conf/common.rmk index 9ca03d4..7586383 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -21,7 +21,7 @@ grub_probe_SOURCES = util/grub-probe.c \ \ partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c \ - disk/lvm.c disk/raid.c grub_probe_init.c + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c ifeq ($(enable_grub_fstest), yes) bin_UTILITIES += grub-fstest @@ -32,7 +32,7 @@ util/grub-fstest.c_DEPENDENCIES = grub_fstest_init.h grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ kern/file.c kern/device.c kern/disk.c kern/err.c kern/misc.c \ disk/host.c disk/loopback.c normal/arg.c normal/misc.c \ - io/gzio.c lib/hexdump.c commands/blocklist.c commands/ls.c \ + lib/hexdump.c lib/crc.c commands/blocklist.c commands/ls.c \ \ fs/affs.c fs/cpio.c fs/ext2.c fs/fat.c fs/hfs.c \ fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ @@ -40,7 +40,9 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ fs/ufs.c fs/xfs.c fs/afs.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ - kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c # For the parser. @@ -270,14 +272,35 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod raid5rec.mod raid6rec.mod mdraid.mod dm_nv.mod \ + lvm.mod # For raid.mod raid_mod_SOURCES = disk/raid.c raid_mod_CFLAGS = $(COMMON_CFLAGS) raid_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For raid.mod +# For raid5rec.mod +raid5rec_mod_SOURCES = disk/raid5_recover.c +raid5rec_mod_CFLAGS = $(COMMON_CFLAGS) +raid5rec_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For raid6rec.mod +raid6rec_mod_SOURCES = disk/raid6_recover.c +raid6rec_mod_CFLAGS = $(COMMON_CFLAGS) +raid6rec_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For lvm.mod lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk index 34b3b40..f3eac0d 100644 --- a/conf/i386-coreboot.rmk +++ b/conf/i386-coreboot.rmk @@ -85,7 +85,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk index ef7d8a2..0b9611f 100644 --- a/conf/i386-efi.rmk +++ b/conf/i386-efi.rmk @@ -62,7 +62,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index d62a656..6477a6f 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -83,7 +83,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index 1ad2e73..1648518 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -102,7 +102,7 @@ grub_setup_SOURCES = util/i386/pc/grub-setup.c util/biosdisk.c \ \ partmap/pc.c partmap/gpt.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/lvm.c \ util/raid.c util/lvm.c \ grub_setup_init.c @@ -142,7 +142,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk index 7e4aaa9..d97207d 100644 --- a/conf/powerpc-ieee1275.rmk +++ b/conf/powerpc-ieee1275.rmk @@ -67,7 +67,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/powerpc/ieee1275/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_script.tab.c grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk index 473c34e..66e9e85 100644 --- a/conf/x86_64-efi.rmk +++ b/conf/x86_64-efi.rmk @@ -64,7 +64,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c new file mode 100644 index 0000000..c2d5ed3 --- /dev/null +++ b/disk/dmraid_nvidia.c @@ -0,0 +1,166 @@ +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +#define NV_SIGNATURES 4 + +#define NV_IDLE 0 +#define NV_SCDB_INIT_RAID 2 +#define NV_SCDB_REBUILD_RAID 3 +#define NV_SCDB_UPGRADE_RAID 4 +#define NV_SCDB_SYNC_RAID 5 + +#define NV_LEVEL_UNKNOWN 0x00 +#define NV_LEVEL_JBOD 0xFF +#define NV_LEVEL_0 0x80 +#define NV_LEVEL_1 0x81 +#define NV_LEVEL_3 0x83 +#define NV_LEVEL_5 0x85 +#define NV_LEVEL_10 0x8a +#define NV_LEVEL_1_0 0x8180 + +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ + +struct grub_nv_array +{ + grub_uint32_t version; + grub_uint32_t signature[NV_SIGNATURES]; + grub_uint8_t raid_job_code; + grub_uint8_t stripe_width; + grub_uint8_t total_volumes; + grub_uint8_t original_width; + grub_uint32_t raid_level; + grub_uint32_t stripe_block_size; + grub_uint32_t stripe_block_size_bytes; + grub_uint32_t stripe_block_size_log2; + grub_uint32_t stripe_mask; + grub_uint32_t stripe_size; + grub_uint32_t stripe_size_bytes; + grub_uint32_t raid_job_mask; + grub_uint32_t original_capacity; + grub_uint32_t flags; +}; + +#define NV_ID_LEN 8 +#define NV_ID_STRING "NVIDIA" +#define NV_VERSION 100 + +#define NV_PRODID_LEN 16 +#define NV_PRODREV_LEN 4 + +struct grub_nv_super +{ + char vendor[NV_ID_LEN]; /* 0x00 - 0x07 ID string. */ + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ + grub_uint8_t reserved; /* 0x13. */ + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ + char prodid[NV_PRODID_LEN]; /* 0x1C - 0x2B Array product ID. */ + char prodrev[NV_PRODREV_LEN]; /* 0x2C - 0x2F Array product revision */ + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ + struct grub_nv_array array; /* Array information */ +} __attribute__ ((packed)); + +static grub_err_t +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + struct grub_nv_super sb; + grub_uint32_t *uuid; + + if (disk->partition) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "skip partition"); + + sector = grub_disk_get_size (disk) - 2; + + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) + return grub_errno; + + if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + if (sb.version != NV_VERSION) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unknown version: %d.%d", sb.version); + + switch (sb.array.raid_level) + { + case NV_LEVEL_0: + array->level = 0; + array->disk_size = sb.capacity / sb.array.total_volumes; + break; + + case NV_LEVEL_1: + array->level = 1; + array->disk_size = sb.capacity; + break; + + case NV_LEVEL_5: + array->level = 5; + array->layout = GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC; + array->disk_size = sb.capacity / (sb.array.total_volumes - 1); + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.array.raid_level); + } + + array->number = 0; + array->total_devs = sb.array.total_volumes; + array->chunk_size = sb.array.stripe_block_size; + array->index = sb.unit_number; + array->uuid_len = sizeof (sb.array.signature); + array->uuid = grub_malloc (sizeof (sb.array.signature)); + if (! array->uuid) + return grub_errno; + + grub_memcpy (array->uuid, (char *) &sb.array.signature, + sizeof (sb.array.signature)); + + return 0; +} + +static struct grub_raid grub_dmraid_nv_dev = +{ + .name = "dmraid_nv", + .detect = grub_dmraid_nv_detect, + .next = 0 +}; + +GRUB_MOD_INIT(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} + +GRUB_MOD_FINI(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..8b7f5ed 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; - total_sectors = 9000000; /* TODO: get the correct size. */ + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c new file mode 100644 index 0000000..a7a5ace --- /dev/null +++ b/disk/mdraid_linux.c @@ -0,0 +1,233 @@ +/* mdraid_linux.c - module to handle linux softraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define RESERVED_BYTES (64 * 1024) +#define RESERVED_SECTORS (RESERVED_BYTES / 512) + +#define NEW_SIZE_SECTORS(x) ((x & ~(RESERVED_SECTORS - 1)) \ + - RESERVED_SECTORS) + +#define SB_BYTES 4096 +#define SB_WORDS (SB_BYTES / 4) +#define SB_SECTORS (SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define SB_GENERIC_OFFSET 0 + +#define SB_PERSONALITY_OFFSET 64 +#define SB_DISKS_OFFSET 128 +#define SB_DESCRIPTOR_OFFSET 992 + +#define SB_GENERIC_CONSTANT_WORDS 32 +#define SB_GENERIC_STATE_WORDS 32 +#define SB_GENERIC_WORDS (SB_GENERIC_CONSTANT_WORDS + \ + SB_GENERIC_STATE_WORDS) + +#define SB_PERSONALITY_WORDS 64 +#define SB_DESCRIPTOR_WORDS 32 +#define SB_DISKS 27 +#define SB_DISKS_WORDS (SB_DISKS * SB_DESCRIPTOR_WORDS) + +#define SB_RESERVED_WORDS (1024 \ + - SB_GENERIC_WORDS \ + - SB_PERSONALITY_WORDS \ + - SB_DISKS_WORDS \ + - SB_DESCRIPTOR_WORDS) + +#define SB_EQUAL_WORDS (SB_GENERIC_WORDS \ + + SB_PERSONALITY_WORDS \ + + SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define DISK_FAULTY 0 +#define DISK_ACTIVE 1 +#define DISK_SYNC 2 +#define DISK_REMOVED 3 + +#define DISK_WRITEMOSTLY 9 + +#define SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define SB_CLEAN 0 +#define SB_ERRORS 1 + +#define SB_BITMAP_PRESENT 8 + +struct grub_raid_disk_09 +{ + grub_uint32_t number; /* Device number in the entire set. */ + grub_uint32_t major; /* Device major number. */ + grub_uint32_t minor; /* Device minor number. */ + grub_uint32_t raid_disk; /* The role of the device in the raid set. */ + grub_uint32_t state; /* Operational state. */ + grub_uint32_t reserved[SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 +{ + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* MD identifier. */ + grub_uint32_t major_version; /* Major version. */ + grub_uint32_t minor_version; /* Minor version. */ + grub_uint32_t patch_version; /* Patchlevel version. */ + grub_uint32_t gvalid_words; /* Number of used words in this section. */ + grub_uint32_t set_uuid0; /* Raid set identifier. */ + grub_uint32_t ctime; /* Creation time. */ + grub_uint32_t level; /* Raid personality. */ + grub_uint32_t size; /* Apparent size of each individual disk. */ + grub_uint32_t nr_disks; /* Total disks in the raid set. */ + grub_uint32_t raid_disks; /* Disks in a fully functional raid set. */ + grub_uint32_t md_minor; /* Preferred MD minor device number. */ + grub_uint32_t not_persistent; /* Does it have a persistent superblock. */ + grub_uint32_t set_uuid1; /* Raid set identifier #2. */ + grub_uint32_t set_uuid2; /* Raid set identifier #3. */ + grub_uint32_t set_uuid3; /* Raid set identifier #4. */ + grub_uint32_t gstate_creserved[SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* Superblock update time. */ + grub_uint32_t state; /* State bits (clean, ...). */ + grub_uint32_t active_disks; /* Number of currently active disks. */ + grub_uint32_t working_disks; /* Number of working disks. */ + grub_uint32_t failed_disks; /* Number of failed disks. */ + grub_uint32_t spare_disks; /* Number of spare disks. */ + grub_uint32_t sb_csum; /* Checksum of the whole superblock. */ + grub_uint64_t events; /* Superblock update count. */ + grub_uint64_t cp_events; /* Checkpoint update count. */ + grub_uint32_t recovery_cp; /* Recovery checkpoint sector count. */ + grub_uint32_t gstate_sreserved[SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* The array's physical layout. */ + grub_uint32_t chunk_size; /* Chunk size in bytes. */ + grub_uint32_t root_pv; /* LV root PV. */ + grub_uint32_t root_block; /* LV root block. */ + grub_uint32_t pstate_reserved[SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +} __attribute__ ((packed)); + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = NEW_SIZE_SECTORS (size); + + if (grub_disk_read (disk, sector, 0, SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != SB_MAGIC) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + /* Multipath. */ + if ((int) sb.level == -4) + sb.level = 1; + + if (sb.level != 0 && sb.level != 1 && sb.level != 4 && + sb.level != 5 && sb.level != 6 && sb.level != 10) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.level); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = sb.layout; + array->total_devs = sb.nr_disks; + array->disk_size = (sb.size) ? sb.size * 2 : sector; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (!array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = { + .name = "mdraid", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT (mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} + +GRUB_MOD_FINI (mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 163bd81..c7c1735 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -26,6 +26,8 @@ /* Linked list of RAID arrays. */ static struct grub_raid_array *array_list; +grub_raid5_recover_func_t grub_raid5_recover_func; +grub_raid6_recover_func_t grub_raid6_recover_func; \f static char @@ -43,10 +45,29 @@ grub_is_array_readable (struct grub_raid_array *array) return 1; break; + case 4: case 5: - if (array->nr_devs >= array->total_devs - 1) - return 1; - break; + case 6: + case 10: + { + unsigned int n; + + if (array->level == 10) + { + n = array->layout & 0xFF; + if (n == 1) + n = (array->layout >> 8) & 0xFF; + + n--; + } + else + n = array->level / 3; + + if (array->nr_devs >= array->total_devs - n) + return 1; + + break; + } } return 0; @@ -91,6 +112,7 @@ static grub_err_t grub_raid_open (const char *name, grub_disk_t disk) { struct grub_raid_array *array; + unsigned n; for (array = array_list; array != NULL; array = array->next) { @@ -100,7 +122,8 @@ grub_raid_open (const char *name, grub_disk_t disk) } if (!array) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s", name); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s", + name); disk->has_partitions = 1; disk->id = array->number; @@ -111,17 +134,27 @@ grub_raid_open (const char *name, grub_disk_t disk) switch (array->level) { - case 0: - /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; - break; - case 1: disk->total_sectors = array->disk_size; break; + case 10: + n = array->layout & 0xFF; + if (n == 1) + n = (array->layout >> 8) & 0xFF; + + disk->total_sectors = grub_divmod64 (array->total_devs * + array->disk_size, + n, 0); + break; + + case 0: + case 4: case 5: - disk->total_sectors = (array->total_devs - 1) * array->disk_size; + case 6: + n = array->level / 3; + + disk->total_sectors = (array->total_devs - n) * array->disk_size; break; } @@ -137,6 +170,22 @@ grub_raid_close (grub_disk_t disk __attribute ((unused))) return; } +void +grub_raid_block_xor (char *buf1, char *buf2, int size) +{ + grub_size_t *p1, *p2; + + p1 = (grub_size_t *) buf1; + p2 = (grub_size_t *) buf2; + size /= GRUB_CPU_SIZEOF_VOID_P; + + while (size) + { + *(p1++) ^= *(p2++); + size--; + } +} + static grub_err_t grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) @@ -147,190 +196,264 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, switch (array->level) { case 0: + case 1: + case 10: { - grub_uint64_t a; - grub_uint32_t b; - unsigned int disknr; - grub_disk_addr_t read_sector; - grub_size_t read_size; - - /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, array->total_devs, &disknr); - - a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; - - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; - - while (1) - { - grub_uint32_t i; - - err = grub_disk_read (array->device[disknr], read_sector, 0, - read_size << GRUB_DISK_SECTOR_BITS, buf); - if (err) - break; - - buf += read_size << GRUB_DISK_SECTOR_BITS; + grub_disk_addr_t read_sector, far_ofs; + grub_uint32_t disknr, b, near, far, ofs; + + read_sector = grub_divmod64 (sector, array->chunk_size, &b); + far = ofs = near = 1; + far_ofs = 0; + + if (array->level == 1) + near = array->total_devs; + else if (array->level == 10) + { + near = array->layout & 0xFF; + far = (array->layout >> 8) & 0xFF; + if (array->layout >> 16) + { + ofs = far; + far_ofs = 1; + } + else + far_ofs = grub_divmod64 (array->disk_size, + far * array->chunk_size, 0); + + far_ofs *= array->chunk_size; + } + + read_sector = grub_divmod64 (read_sector * near, array->total_devs, + &disknr); + + ofs *= array->chunk_size; + read_sector *= ofs; + + while (1) + { + grub_size_t read_size; + unsigned int i, j; + + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + for (i = 0; i < near; i++) + { + unsigned int k; + + k = disknr; + for (j = 0; j < far; j++) + { + if (array->device[k]) + { + if (grub_errno == GRUB_ERR_READ_ERROR) + grub_errno = GRUB_ERR_NONE; + + err = grub_disk_read (array->device[k], + read_sector + j * far_ofs + b, + 0, + read_size << GRUB_DISK_SECTOR_BITS, + buf); + if (! err) + break; + else if (err != GRUB_ERR_READ_ERROR) + return err; + } + else + err = grub_error (GRUB_ERR_READ_ERROR, + "disk missing."); + + k++; + if (k == array->total_devs) + k = 0; + } + + if (! err) + break; + + disknr++; + if (disknr == array->total_devs) + { + disknr = 0; + read_sector += ofs; + } + } + + if (err) + return err; + + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - - disknr++; - /* See whether the disk was the last disk, and start - reading from the first disk in that case. */ - if (disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - } - } - } - break; - - case 1: - /* This is easy, we can read from any disk we want. We will loop - over all disks until we've found one that is available. In - case of errs, we will try the to read the next disk. */ - { - unsigned int i = 0; - - for (i = 0; i < array->total_devs; i++) - { - if (array->device[i]) - { - err = grub_disk_read (array->device[i], sector, 0, - size << GRUB_DISK_SECTOR_BITS, buf); - - if (!err) - break; - } - } + b = 0; + disknr += (near - i); + while (disknr >= array->total_devs) + { + disknr -= array->total_devs; + read_sector += ofs; + } + } + break; } - break; + case 4: case 5: + case 6: { - grub_uint64_t a; - grub_uint32_t b; - int disknr; grub_disk_addr_t read_sector; - grub_size_t read_size; - - /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, (array->total_devs - 1), &b); - disknr = b; + grub_uint32_t b, p, n, disknr, e; - a = grub_divmod64 (sector, array->chunk_size * (array->total_devs - 1), - NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; + /* n = 1 for level 4 and 5, 2 for level 6. */ + n = array->level / 3; - grub_divmod64 (read_sector, array->chunk_size * array->total_devs, &b); - disknr -= (b / array->chunk_size); - if (disknr < 0) - disknr += array->total_devs; - - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; + /* Find the first sector to read. */ + read_sector = grub_divmod64 (sector, array->chunk_size, &b); + read_sector = grub_divmod64 (read_sector, array->total_devs - n, + &disknr); + if (array->level >= 5) + { + grub_divmod64 (read_sector, array->total_devs, &p); + + if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)) + p = array->total_devs - 1 - p; + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr += p + n; + } + else + { + grub_uint32_t q; + + q = p + (n - 1); + if (q >= array->total_devs) + q -= array->total_devs; + + if (disknr >= p) + disknr += n; + else if (disknr >= q) + disknr += q + 1; + } + + if (disknr >= array->total_devs) + disknr -= array->total_devs; + } + else + p = array->total_devs - n; + + read_sector *= array->chunk_size; while (1) { - grub_uint32_t i; - - if (array->device[disknr]) - err = grub_disk_read (array->device[disknr], read_sector, 0, - read_size << GRUB_DISK_SECTOR_BITS, buf); + grub_size_t read_size; + int next_level; + + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + e = 0; + if (array->device[disknr]) + { + /* Reset read error. */ + if (grub_errno == GRUB_ERR_READ_ERROR) + grub_errno = GRUB_ERR_NONE; + + err = grub_disk_read (array->device[disknr], + read_sector + b, 0, + read_size << GRUB_DISK_SECTOR_BITS, + buf); + + if ((err) && (err != GRUB_ERR_READ_ERROR)) + break; + e++; + } + else + err = GRUB_ERR_READ_ERROR; - /* If an error occurs when we already have an degraded - array we can't recover from that. */ - if (err && ((array->total_devs - 1) == array->nr_devs)) - break; + if (err) + { + if (array->nr_devs < array->total_devs - n + e) + break; + + grub_errno = GRUB_ERR_NONE; + if (array->level == 6) + { + err = ((grub_raid6_recover_func) ? + (*grub_raid6_recover_func) (array, disknr, p, + buf, read_sector + b, + read_size) : + grub_error (GRUB_ERR_BAD_DEVICE, + "raid6rec is not loaded")); + } + else + { + err = ((grub_raid5_recover_func) ? + (*grub_raid5_recover_func) (array, disknr, + buf, read_sector + b, + read_size) : + grub_error (GRUB_ERR_BAD_DEVICE, + "raid5rec is not loaded")); + } + + if (err) + break; + } - if (err || ! array->device[disknr]) - { - /* Either an error occured or the disk is not - available. We have to compute this block from the - blocks on the other hard disks. */ - grub_size_t buf_size = read_size << GRUB_DISK_SECTOR_BITS; - char buf2[buf_size]; - unsigned int j; - - grub_memset (buf, 0, buf_size); - - for (j = 0; j < array->total_devs; j++) - { - unsigned int k; - - if (j != (unsigned int) disknr) - { - err = grub_disk_read (array->device[j], read_sector, - 0, buf_size, buf2); - if (err) - return err; - - for (k = 0; k < buf_size; k++) - buf[k] = buf[k] ^ buf2[k]; - } - } - } - - buf += (read_size << GRUB_DISK_SECTOR_BITS); + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - + b = 0; disknr++; - grub_divmod64 (read_sector, - array->chunk_size * array->total_devs, &i); - if ((unsigned int) disknr == (array->total_devs - (i / array->chunk_size) - 1)) - disknr++; - /* See whether the disk was the last disk, and start - reading from the first disk in that case. */ - if ((unsigned int) disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - grub_divmod64 (read_sector, - array->chunk_size * array->total_devs, &i); - - if ((i / array->chunk_size) == (array->total_devs - 1)) - disknr++; - } + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + if (disknr == array->total_devs) + disknr = 0; + + next_level = (disknr == p); + } + else + { + if (disknr == p) + disknr += n; + + next_level = (disknr >= array->total_devs); + } + + if (next_level) + { + read_sector += array->chunk_size; + + if (array->level >= 5) + { + if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK) + p = (p == array->total_devs - 1) ? 0 : p + 1; + else + p = (p == 0) ? array->total_devs - 1 : p - 1; + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr = p + n; + if (disknr >= array->total_devs) + disknr -= array->total_devs; + } + else + { + disknr -= array->total_devs; + if (disknr == p) + disknr += n; + } + } + else + disknr = 0; + } } } break; @@ -348,169 +471,105 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *scanner_name) { - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); - - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } - - /* FIXME: Check the checksum. */ - - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } - - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + if (new_array->disk_size < array->disk_size) + array->disk_size = new_array->disk_size; + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } array->name = grub_malloc (13); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } grub_sprintf (array->name, "md%d", array->number); - grub_dprintf ("raid", "Found array: %s\n", array->name); + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, + scanner_name); /* Add our new array to the list. */ array->next = array_list; @@ -518,47 +577,116 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; + + return 0; +} + +static grub_raid_t grub_raid_list; - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) +static void +grub_raid_scan_device (int head_only) +{ + auto int hook (const char *name); + int hook (const char *name) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); + + disk = grub_disk_open (name); + if (!disk) + return 0; + + if (disk->total_sectors == ULONG_MAX) + { + grub_disk_close (disk); + return 0; + } + + for (p = grub_raid_list; p; p = p->next) + { + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + return 0; + + break; + } + + /* This error usually means it's not raid, no need to display + it. */ + if (grub_errno != GRUB_ERR_OUT_OF_RANGE) + grub_print_error (); + + grub_errno = GRUB_ERR_NONE; + if (head_only) + break; + } + + grub_disk_close (disk); + + return 0; } - if (! array->device[sb.this_disk.number]) + grub_device_iterate (&hook); +} + +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + struct grub_raid_array *p; + int i; - return 0; + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); } - array->nr_devs++; + array_list = 0; +} - return 0; +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; + grub_raid_scan_device (1); +} + +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; + + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} + +void +grub_raid_rescan (void) +{ + free_array (); + grub_raid_scan_device (0); } static struct grub_disk_dev grub_raid_dev = @@ -579,12 +707,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/disk/raid5_recover.c b/disk/raid5_recover.c new file mode 100755 index 0000000..31cef88 --- /dev/null +++ b/disk/raid5_recover.c @@ -0,0 +1,72 @@ +/* raid5_recover.c - module to recover from faulty RAID4/5 arrays. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +static grub_err_t +grub_raid5_recover (struct grub_raid_array *array, int disknr, + char *buf, grub_disk_addr_t sector, int size) +{ + char *buf2; + int i; + + size <<= GRUB_DISK_SECTOR_BITS; + buf2 = grub_malloc (size); + if (!buf2) + return grub_errno; + + grub_memset (buf, 0, size); + + for (i = 0; i < (int) array->total_devs; i++) + { + grub_err_t err; + + if (i == disknr) + continue; + + err = grub_disk_read (array->device[i], sector, 0, size, buf2); + + if (err) + { + grub_free (buf2); + return err; + } + + grub_raid_block_xor (buf, buf2, size); + } + + grub_free (buf2); + + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT(raid5rec) +{ + grub_raid5_recover_func = grub_raid5_recover; +} + +GRUB_MOD_FINI(raid5rec) +{ + grub_raid5_recover_func = 0; +} diff --git a/disk/raid6_recover.c b/disk/raid6_recover.c new file mode 100755 index 0000000..b536d19 --- /dev/null +++ b/disk/raid6_recover.c @@ -0,0 +1,216 @@ +/* raid6_recover.c - module to recover from faulty RAID6 arrays. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +static grub_uint8_t raid6_table1[256][256]; +static grub_uint8_t raid6_table2[256][256]; + +static void +grub_raid_block_mul (grub_uint8_t mul, char *buf, int size) +{ + int i; + grub_uint8_t *p; + + p = (grub_uint8_t *) buf; + for (i = 0; i < size; i++, p++) + *p = raid6_table1[mul][*p]; +} + +static void +grub_raid6_init_table (void) +{ + int i, j; + + for (i = 0; i < 256; i++) + raid6_table1[i][1] = raid6_table1[1][i] = i; + + for (i = 2; i < 256; i++) + for (j = i; j < 256; j++) + { + int n; + grub_uint8_t c; + + n = i >> 1; + + c = raid6_table1[n][j]; + c = (c << 1) ^ ((c & 0x80) ? 0x1d : 0); + if (i & 1) + c ^= j; + + raid6_table1[j][i] = raid6_table1[i][j] = c; + } + + raid6_table2[0][0] = 1; + for (i = 1; i < 256; i++) + raid6_table2[i][i] = raid6_table1[raid6_table2[i - 1][i - 1]][2]; + + for (i = 0; i < 254; i++) + for (j = 0; j < 254; j++) + { + grub_uint8_t c, n; + int k; + + if (i == j) + continue; + + k = i - j; + if (k < 0) + k += 255; + + c = n = raid6_table2[k][k] ^ 1; + for (k = 0; k < 253; k++) + c = raid6_table1[c][n]; + + raid6_table2[i][j] = raid6_table1[raid6_table2[255 - j][255 - j]][c]; + } +} + +static grub_err_t +grub_raid6_recover (struct grub_raid_array *array, int disknr, int p, + char *buf, grub_disk_addr_t sector, int size) +{ + int i, q, pos; + int err[2], nerr; + char *pbuf = 0, *qbuf = 0; + + size <<= GRUB_DISK_SECTOR_BITS; + pbuf = grub_malloc (size); + if (!pbuf) + goto quit; + + qbuf = grub_malloc (size); + if (!qbuf) + goto quit; + + q = p + 1; + if (q == (int) array->total_devs) + q = 0; + + grub_memset (pbuf, 0, size); + grub_memset (qbuf, 0, size); + + pos = q + 1; + if (pos == (int) array->total_devs) + pos = 0; + + nerr = 1; + for (i = 0; i < (int) array->total_devs - 2; i++) + { + if (pos == disknr) + err[0] = i; + else + { + if ((array->device[pos]) && + (! grub_disk_read (array->device[pos], sector, 0, size, buf))) + { + grub_raid_block_xor (pbuf, buf, size); + grub_raid_block_mul (raid6_table2[i][i], buf, size); + grub_raid_block_xor (qbuf, buf, size); + } + else + { + if (nerr >= 2) + goto quit; + + err[nerr++] = i; + grub_errno = GRUB_ERR_NONE; + } + } + + pos++; + if (pos == (int) array->total_devs) + pos = 0; + } + + if (nerr == 1) + { + if ((array->device[p]) && + (! grub_disk_read (array->device[p], sector, 0, size, buf))) + { + grub_raid_block_xor (buf, pbuf, size); + goto quit; + } + + if (! array->device[q]) + { + grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore"); + goto quit; + } + + grub_errno = GRUB_ERR_NONE; + if (grub_disk_read (array->device[q], sector, 0, size, buf)) + goto quit; + + grub_raid_block_xor (buf, qbuf, size); + grub_raid_block_mul (raid6_table2[255 - err[0]][255 - err[0]], buf, + size); + } + else + { + grub_uint8_t c; + + if ((! array->device[p]) || (! array->device[q])) + { + grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore"); + goto quit; + } + + if (grub_disk_read (array->device[p], sector, 0, size, buf)) + goto quit; + + grub_raid_block_xor (pbuf, buf, size); + + if (grub_disk_read (array->device[q], sector, 0, size, buf)) + goto quit; + + grub_raid_block_xor (qbuf, buf, size); + + c = raid6_table2[err[1]][err[0]]; + grub_raid_block_mul (c, qbuf, size); + + c = raid6_table1[raid6_table2[err[1]][err[1]]][c]; + grub_raid_block_mul (c, pbuf, size); + + grub_raid_block_xor (pbuf, qbuf, size); + grub_memcpy (buf, pbuf, size); + } + +quit: + grub_free (pbuf); + grub_free (qbuf); + + return grub_errno; +} + +GRUB_MOD_INIT(raid6rec) +{ + grub_raid6_init_table (); + grub_raid6_recover_func = grub_raid6_recover; +} + +GRUB_MOD_FINI(raid6rec) +{ + grub_raid6_recover_func = 0; +} diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..a36be6d 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,65 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + +#define GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC 0 +#define GRUB_RAID_LAYOUT_RIGHT_ASYMMETRIC 1 +#define GRUB_RAID_LAYOUT_LEFT_SYMMETRIC 2 +#define GRUB_RAID_LAYOUT_RIGHT_SYMMETRIC 3 + +#define GRUB_RAID_LAYOUT_RIGHT_MASK 1 +#define GRUB_RAID_LAYOUT_SYMMETRIC_MASK 2 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ - int layout; /* Only for RAID 5. */ + int layout; /* Layout for RAID 5/6. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) +struct grub_raid +{ + const char *name; -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + struct grub_raid *next; +}; +typedef struct grub_raid *grub_raid_t; -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); +void grub_raid_rescan (void); +void grub_raid_block_xor (char *buf1, char *buf2, int size); -#define GRUB_RAID_SB_MAGIC 0xa92b4efc +typedef grub_err_t (*grub_raid5_recover_func_t) (struct grub_raid_array *array, + int disknr, char *buf, + grub_disk_addr_t sector, + int size); -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 - -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ - -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; -}; +typedef grub_err_t (*grub_raid6_recover_func_t) (struct grub_raid_array *array, + int disknr, int p, char *buf, + grub_disk_addr_t sector, + int size); -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; - - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +extern grub_raid5_recover_func_t grub_raid5_recover_func; +extern grub_raid6_recover_func_t grub_raid6_recover_func; #endif /* ! GRUB_RAID_H */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..a1c3c89 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,7 +29,9 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> +#include <grub/lib/crc.h> #include <grub_fstest_init.h> @@ -136,12 +138,12 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define CMD_CP 2 #define CMD_CMP 3 #define CMD_HEX 4 -#define CMD_BLOCKLIST 5 +#define CMD_CRC 6 +#define CMD_BLOCKLIST 7 #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -150,16 +152,53 @@ read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) grub_file_t file; grub_off_t ofs, len; + if ((pathname[0] == '-') && (pathname[1] == 0)) + { + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device."); + + grub_util_info ("total sectors : %lld.", + (unsigned long long) dev->disk->total_sectors); + + if (! leng) + leng = (dev->disk->total_sectors << GRUB_DISK_SECTOR_BITS) - skip; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d.", + skip, len); + + if (hook (skip, buf, len)) + break; + + skip += len; + leng -= len; + } + + grub_device_close (dev); + return; + } + file = grub_file_open (pathname); if (!file) { - grub_util_error ("cannot open file %s.\n", pathname); + grub_util_error ("cannot open file %s.", pathname); return; } + grub_util_info ("file size : %lld.", (unsigned long long) file->size); + if (skip > file->size) { - grub_util_error ("invalid skip value %d.\n"); + grub_util_error ("invalid skip value %d."); return; } @@ -177,7 +216,7 @@ read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) sz = grub_file_read (file, buf, (len > BUF_SIZE) ? BUF_SIZE : len); if (sz < 0) { - grub_util_error ("read error at offset %llu.\n", ofs); + grub_util_error ("read error at offset %llu.", ofs); break; } @@ -203,7 +242,7 @@ cmd_cp (char *src, char *dest) if ((int) fwrite (buf, 1, len, ff) != len) { - grub_util_error ("write error.\n"); + grub_util_error ("write error."); return 1; } @@ -213,7 +252,7 @@ cmd_cp (char *src, char *dest) ff = fopen (dest, "w"); if (ff == NULL) { - grub_util_error ("open error.\n"); + grub_util_error ("open error."); return; } read_file (src, cp_hook); @@ -231,7 +270,7 @@ cmd_cmp (char *src, char *dest) { if ((int) fread (buf_1, 1, len, ff) != len) { - grub_util_error ("read error at offset %llu.\n", ofs); + grub_util_error ("read error at offset %llu.", ofs); return 1; } @@ -242,7 +281,7 @@ cmd_cmp (char *src, char *dest) for (i = 0; i < len; i++, ofs++) if (buf_1[i] != buf[i]) { - grub_util_error ("compare fail at offset %llu.\n", ofs); + grub_util_error ("compare fail at offset %llu.", ofs); return 1; } } @@ -252,12 +291,12 @@ cmd_cmp (char *src, char *dest) ff = fopen (dest, "r"); if (ff == NULL) { - grub_util_error ("open error.\n"); + grub_util_error ("open error."); return; } if ((skip) && (fseeko (ff, skip, SEEK_SET))) - grub_util_error ("seek error.\n"); + grub_util_error ("seek error."); read_file (src, cmp_hook); fclose (ff); @@ -277,28 +316,44 @@ cmd_hex (char *pathname) } static void -fstest (char *image_path, int cmd, int n, char **args) +cmd_crc (char *pathname) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; + grub_uint32_t crc = 0; + + auto int crc_hook (grub_off_t ofs, char *buf, int len); + int crc_hook (grub_off_t ofs, char *buf, int len) + { + (void) ofs; + + crc = grub_getcrc32 (crc, buf, len); + return 0; + } + read_file (pathname, crc_hook); + printf ("%08x\n", crc); +} - grub_sprintf (host_file, "(host)/%s", image_path); +static void +fstest (char **images, int num_disks, int cmd, int n, char **args) +{ + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long.", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails."); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -313,29 +368,32 @@ fstest (char *image_path, int cmd, int n, char **args) case CMD_HEX: cmd_hex (args[0]); break; + case CMD_CRC: + cmd_crc (args[0]); + break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -351,17 +409,17 @@ Usage: grub-fstest [OPTION]... IMAGE_PATH COMMANDS\n\ Debug tool for filesystem driver.\n\ \nCommands:\n\ ls PATH list files in PATH\n\ - cp SRC DEST copy file to local system\n\ - cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + cp FILE LOCAL copy FILE to local file LOCAL\n\ + cmp FILE LOCAL compare FILE with local file LOCAL\n\ + hex FILE Hex dump FILE\n\ + crc FILE Get crc32 checksum of FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +432,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,52 +511,53 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "No path is specified.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - image_path = argv[optind]; - - if (*image_path != '/') - { - fprintf (stderr, "Must use absolute path.\n"); - usage (1); - } - - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; - nparm = 2; + nparm = 2; } else if (!grub_strcmp (argv[optind], "cmp")) { cmd = CMD_CMP; - nparm = 2; + nparm = 2; } else if (!grub_strcmp (argv[optind], "hex")) { cmd = CMD_HEX; + nparm = 1; + } + else if (!grub_strcmp (argv[optind], "crc")) + { + cmd = CMD_CRC; + nparm = 1; } else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +583,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); diff --git a/util/grub-probe.c b/util/grub-probe.c index a4f51e2..ce9cbff 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -142,7 +142,7 @@ probe (const char *path, char *device_name) abstraction_name = "lvm"; break; case GRUB_DEV_ABSTRACTION_RAID: - abstraction_name = "raid"; + abstraction_name = "raid mdraid"; break; default: grub_util_info ("did not find LVM/RAID in %s, assuming raw device", device_name); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-16 15:20 ` Bean 2008-08-17 14:19 ` Bean @ 2008-08-17 14:52 ` Robert Millan 2008-08-17 15:08 ` Bean 1 sibling, 1 reply; 24+ messages in thread From: Robert Millan @ 2008-08-17 14:52 UTC (permalink / raw) To: The development of GRUB 2 On Sat, Aug 16, 2008 at 11:20:04PM +0800, Bean wrote: > On Sat, Aug 16, 2008 at 8:16 PM, Robert Millan <rmh@aybabtu.com> wrote: > > On Sat, Aug 16, 2008 at 05:07:00PM +0800, Bean wrote: > >> Hi, > >> > >> This new patch seperates raid5 and raid6 recover code from raid.c, and > >> place them in module raid5rec.mod and raid6rec.mod. The recover code > >> is only needed when some of the disk are missing or corrupted, which > >> is not common. But raid.c is installed to mbr, so size is important. > >> If there is enough room in mbr for the extra module, they can use > >> --modules option in grub-install to add these modules. > > > > I find this scary in the sense that users need to know about these modules > > to get the benefit of recovery, and even then, they also need to know they > > are affected by this problem when they install GRUB (since otherwise they > > won't be able to bootstrap). > > > > Is it possible to detect whether recover code will be needed when grub-install > > is run, and then either add the extra modules or abort with an error? > > Hi, > > grub-probe don't use the recover module, so if it have problem at > install time, user would know about it (error message would be > "raid5rec not loaded"). Sounds fine to me. But the error message is too cryptic IMHO. Consider the situation in which user was running grub-install and sees this error. Shouldn't we tell her to fix the RAID instead? -- Robert Millan The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and how) you may access your data; but nobody's threatening your freedom: we still allow you to remove your data and not access it at all." ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-17 14:52 ` Robert Millan @ 2008-08-17 15:08 ` Bean 2008-08-17 15:23 ` Robert Millan 0 siblings, 1 reply; 24+ messages in thread From: Bean @ 2008-08-17 15:08 UTC (permalink / raw) To: The development of GRUB 2 On Sun, Aug 17, 2008 at 10:52 PM, Robert Millan <rmh@aybabtu.com> wrote: > On Sat, Aug 16, 2008 at 11:20:04PM +0800, Bean wrote: >> On Sat, Aug 16, 2008 at 8:16 PM, Robert Millan <rmh@aybabtu.com> wrote: >> > On Sat, Aug 16, 2008 at 05:07:00PM +0800, Bean wrote: >> >> Hi, >> >> >> >> This new patch seperates raid5 and raid6 recover code from raid.c, and >> >> place them in module raid5rec.mod and raid6rec.mod. The recover code >> >> is only needed when some of the disk are missing or corrupted, which >> >> is not common. But raid.c is installed to mbr, so size is important. >> >> If there is enough room in mbr for the extra module, they can use >> >> --modules option in grub-install to add these modules. >> > >> > I find this scary in the sense that users need to know about these modules >> > to get the benefit of recovery, and even then, they also need to know they >> > are affected by this problem when they install GRUB (since otherwise they >> > won't be able to bootstrap). >> > >> > Is it possible to detect whether recover code will be needed when grub-install >> > is run, and then either add the extra modules or abort with an error? >> >> Hi, >> >> grub-probe don't use the recover module, so if it have problem at >> install time, user would know about it (error message would be >> "raid5rec not loaded"). > > Sounds fine to me. But the error message is too cryptic IMHO. Consider the > situation in which user was running grub-install and sees this error. > Shouldn't we tell her to fix the RAID instead? Hi, How about "please load raid5rec.mod to enable raid5 recovery" ? -- Bean ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-17 15:08 ` Bean @ 2008-08-17 15:23 ` Robert Millan 2008-08-18 17:20 ` Bean 0 siblings, 1 reply; 24+ messages in thread From: Robert Millan @ 2008-08-17 15:23 UTC (permalink / raw) To: The development of GRUB 2 On Sun, Aug 17, 2008 at 11:08:07PM +0800, Bean wrote: > >> > >> grub-probe don't use the recover module, so if it have problem at > >> install time, user would know about it (error message would be > >> "raid5rec not loaded"). > > > > Sounds fine to me. But the error message is too cryptic IMHO. Consider the > > situation in which user was running grub-install and sees this error. > > Shouldn't we tell her to fix the RAID instead? > > Hi, > > How about "please load raid5rec.mod to enable raid5 recovery" ? Ah, I was thinking the recommended solution was to fix the RAID instead so that it won't need recovery. Are you sure asking to have raid5rec loaded is a good idea? It might not fit, which IIUC was the problem we were trying to avoid. -- Robert Millan The DRM opt-in fallacy: "Your data belongs to us. We will decide when (and how) you may access your data; but nobody's threatening your freedom: we still allow you to remove your data and not access it at all." ^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-17 15:23 ` Robert Millan @ 2008-08-18 17:20 ` Bean 2008-08-23 14:51 ` Bean 0 siblings, 1 reply; 24+ messages in thread From: Bean @ 2008-08-18 17:20 UTC (permalink / raw) To: The development of GRUB 2 [-- Attachment #1: Type: text/plain, Size: 128 bytes --] Hi, This patch fix a bug in total_devs and grub_raid_memberlist, it also resolve the conflict with current svn head. -- Bean [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: raid_11.diff --] [-- Type: text/x-diff; name=raid_11.diff, Size: 75454 bytes --] diff --git a/conf/common.rmk b/conf/common.rmk index 9ca03d4..7586383 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -21,7 +21,7 @@ grub_probe_SOURCES = util/grub-probe.c \ \ partmap/pc.c partmap/apple.c partmap/gpt.c \ kern/fs.c kern/env.c fs/fshelp.c \ - disk/lvm.c disk/raid.c grub_probe_init.c + disk/lvm.c disk/raid.c disk/mdraid_linux.c grub_probe_init.c ifeq ($(enable_grub_fstest), yes) bin_UTILITIES += grub-fstest @@ -32,7 +32,7 @@ util/grub-fstest.c_DEPENDENCIES = grub_fstest_init.h grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ kern/file.c kern/device.c kern/disk.c kern/err.c kern/misc.c \ disk/host.c disk/loopback.c normal/arg.c normal/misc.c \ - io/gzio.c lib/hexdump.c commands/blocklist.c commands/ls.c \ + lib/hexdump.c lib/crc.c commands/blocklist.c commands/ls.c \ \ fs/affs.c fs/cpio.c fs/ext2.c fs/fat.c fs/hfs.c \ fs/hfsplus.c fs/iso9660.c fs/udf.c fs/jfs.c fs/minix.c \ @@ -40,7 +40,9 @@ grub_fstest_SOURCES = util/grub-fstest.c util/hostfs.c util/misc.c \ fs/ufs.c fs/xfs.c fs/afs.c \ \ kern/partition.c partmap/pc.c partmap/apple.c partmap/gpt.c \ - kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + kern/fs.c kern/env.c fs/fshelp.c disk/lvm.c disk/raid.c \ + disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c \ grub_fstest_init.c # For the parser. @@ -270,14 +272,35 @@ gpt_mod_LDFLAGS = $(COMMON_LDFLAGS) # Special disk structures -pkglib_MODULES += raid.mod lvm.mod +pkglib_MODULES += raid.mod raid5rec.mod raid6rec.mod mdraid.mod dm_nv.mod \ + lvm.mod # For raid.mod raid_mod_SOURCES = disk/raid.c raid_mod_CFLAGS = $(COMMON_CFLAGS) raid_mod_LDFLAGS = $(COMMON_LDFLAGS) -# For raid.mod +# For raid5rec.mod +raid5rec_mod_SOURCES = disk/raid5_recover.c +raid5rec_mod_CFLAGS = $(COMMON_CFLAGS) +raid5rec_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For raid6rec.mod +raid6rec_mod_SOURCES = disk/raid6_recover.c +raid6rec_mod_CFLAGS = $(COMMON_CFLAGS) +raid6rec_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For mdraid.mod +mdraid_mod_SOURCES = disk/mdraid_linux.c +mdraid_mod_CFLAGS = $(COMMON_CFLAGS) +mdraid_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For dm_nv.mod +dm_nv_mod_SOURCES = disk/dmraid_nvidia.c +dm_nv_mod_CFLAGS = $(COMMON_CFLAGS) +dm_nv_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For lvm.mod lvm_mod_SOURCES = disk/lvm.c lvm_mod_CFLAGS = $(COMMON_CFLAGS) lvm_mod_LDFLAGS = $(COMMON_LDFLAGS) diff --git a/conf/i386-coreboot.rmk b/conf/i386-coreboot.rmk index 34b3b40..f3eac0d 100644 --- a/conf/i386-coreboot.rmk +++ b/conf/i386-coreboot.rmk @@ -85,7 +85,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-efi.rmk b/conf/i386-efi.rmk index ef7d8a2..0b9611f 100644 --- a/conf/i386-efi.rmk +++ b/conf/i386-efi.rmk @@ -62,7 +62,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-ieee1275.rmk b/conf/i386-ieee1275.rmk index d62a656..6477a6f 100644 --- a/conf/i386-ieee1275.rmk +++ b/conf/i386-ieee1275.rmk @@ -83,7 +83,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/i386-pc.rmk b/conf/i386-pc.rmk index a2ab94d..d2773dc 100644 --- a/conf/i386-pc.rmk +++ b/conf/i386-pc.rmk @@ -103,7 +103,7 @@ grub_setup_SOURCES = util/i386/pc/grub-setup.c util/biosdisk.c \ \ partmap/pc.c partmap/gpt.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/mdraid_linux.c disk/lvm.c \ util/raid.c util/lvm.c \ grub_setup_init.c @@ -143,7 +143,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/powerpc-ieee1275.rmk b/conf/powerpc-ieee1275.rmk index 7e4aaa9..d97207d 100644 --- a/conf/powerpc-ieee1275.rmk +++ b/conf/powerpc-ieee1275.rmk @@ -67,7 +67,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/powerpc/ieee1275/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_script.tab.c grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/conf/x86_64-efi.rmk b/conf/x86_64-efi.rmk index 473c34e..66e9e85 100644 --- a/conf/x86_64-efi.rmk +++ b/conf/x86_64-efi.rmk @@ -64,7 +64,8 @@ grub_emu_SOURCES = commands/boot.c commands/cat.c commands/cmp.c \ util/biosdisk.c util/getroot.c \ util/i386/pc/misc.c \ \ - disk/raid.c disk/lvm.c \ + disk/raid.c disk/raid5_recover.c disk/raid6_recover.c \ + disk/mdraid_linux.c disk/dmraid_nvidia.c disk/lvm.c \ grub_emu_init.c grub_emu_LDFLAGS = $(LIBCURSES) diff --git a/disk/dmraid_nvidia.c b/disk/dmraid_nvidia.c new file mode 100644 index 0000000..c2d5ed3 --- /dev/null +++ b/disk/dmraid_nvidia.c @@ -0,0 +1,166 @@ +/* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +#define NV_SIGNATURES 4 + +#define NV_IDLE 0 +#define NV_SCDB_INIT_RAID 2 +#define NV_SCDB_REBUILD_RAID 3 +#define NV_SCDB_UPGRADE_RAID 4 +#define NV_SCDB_SYNC_RAID 5 + +#define NV_LEVEL_UNKNOWN 0x00 +#define NV_LEVEL_JBOD 0xFF +#define NV_LEVEL_0 0x80 +#define NV_LEVEL_1 0x81 +#define NV_LEVEL_3 0x83 +#define NV_LEVEL_5 0x85 +#define NV_LEVEL_10 0x8a +#define NV_LEVEL_1_0 0x8180 + +#define NV_ARRAY_FLAG_BOOT 1 /* BIOS use only. */ +#define NV_ARRAY_FLAG_ERROR 2 /* Degraded or offling. */ +#define NV_ARRAY_FLAG_PARITY_VALID 4 /* RAID-3/5 parity valid. */ + +struct grub_nv_array +{ + grub_uint32_t version; + grub_uint32_t signature[NV_SIGNATURES]; + grub_uint8_t raid_job_code; + grub_uint8_t stripe_width; + grub_uint8_t total_volumes; + grub_uint8_t original_width; + grub_uint32_t raid_level; + grub_uint32_t stripe_block_size; + grub_uint32_t stripe_block_size_bytes; + grub_uint32_t stripe_block_size_log2; + grub_uint32_t stripe_mask; + grub_uint32_t stripe_size; + grub_uint32_t stripe_size_bytes; + grub_uint32_t raid_job_mask; + grub_uint32_t original_capacity; + grub_uint32_t flags; +}; + +#define NV_ID_LEN 8 +#define NV_ID_STRING "NVIDIA" +#define NV_VERSION 100 + +#define NV_PRODID_LEN 16 +#define NV_PRODREV_LEN 4 + +struct grub_nv_super +{ + char vendor[NV_ID_LEN]; /* 0x00 - 0x07 ID string. */ + grub_uint32_t size; /* 0x08 - 0x0B Size of metadata in dwords. */ + grub_uint32_t chksum; /* 0x0C - 0x0F Checksum of this struct. */ + grub_uint16_t version; /* 0x10 - 0x11 NV version. */ + grub_uint8_t unit_number; /* 0x12 Disk index in array. */ + grub_uint8_t reserved; /* 0x13. */ + grub_uint32_t capacity; /* 0x14 - 0x17 Array capacity in sectors. */ + grub_uint32_t sector_size; /* 0x18 - 0x1B Sector size. */ + char prodid[NV_PRODID_LEN]; /* 0x1C - 0x2B Array product ID. */ + char prodrev[NV_PRODREV_LEN]; /* 0x2C - 0x2F Array product revision */ + grub_uint32_t unit_flags; /* 0x30 - 0x33 Flags for this disk */ + struct grub_nv_array array; /* Array information */ +} __attribute__ ((packed)); + +static grub_err_t +grub_dmraid_nv_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + struct grub_nv_super sb; + grub_uint32_t *uuid; + + if (disk->partition) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "skip partition"); + + sector = grub_disk_get_size (disk) - 2; + + if (grub_disk_read (disk, sector, 0, sizeof (sb), (char *) &sb)) + return grub_errno; + + if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + if (sb.version != NV_VERSION) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unknown version: %d.%d", sb.version); + + switch (sb.array.raid_level) + { + case NV_LEVEL_0: + array->level = 0; + array->disk_size = sb.capacity / sb.array.total_volumes; + break; + + case NV_LEVEL_1: + array->level = 1; + array->disk_size = sb.capacity; + break; + + case NV_LEVEL_5: + array->level = 5; + array->layout = GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC; + array->disk_size = sb.capacity / (sb.array.total_volumes - 1); + break; + + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.array.raid_level); + } + + array->number = 0; + array->total_devs = sb.array.total_volumes; + array->chunk_size = sb.array.stripe_block_size; + array->index = sb.unit_number; + array->uuid_len = sizeof (sb.array.signature); + array->uuid = grub_malloc (sizeof (sb.array.signature)); + if (! array->uuid) + return grub_errno; + + grub_memcpy (array->uuid, (char *) &sb.array.signature, + sizeof (sb.array.signature)); + + return 0; +} + +static struct grub_raid grub_dmraid_nv_dev = +{ + .name = "dmraid_nv", + .detect = grub_dmraid_nv_detect, + .next = 0 +}; + +GRUB_MOD_INIT(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} + +GRUB_MOD_FINI(dm_nv) +{ + grub_raid_register (&grub_dmraid_nv_dev); +} diff --git a/disk/i386/pc/biosdisk.c b/disk/i386/pc/biosdisk.c index c8fd142..8b7f5ed 100644 --- a/disk/i386/pc/biosdisk.c +++ b/disk/i386/pc/biosdisk.c @@ -120,7 +120,7 @@ grub_biosdisk_open (const char *name, grub_disk_t disk) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; - total_sectors = 9000000; /* TODO: get the correct size. */ + total_sectors = ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { diff --git a/disk/mdraid_linux.c b/disk/mdraid_linux.c new file mode 100644 index 0000000..163fab7 --- /dev/null +++ b/disk/mdraid_linux.c @@ -0,0 +1,233 @@ +/* mdraid_linux.c - module to handle linux softraid. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +/* Linux RAID on disk structures and constants, + copied from include/linux/raid/md_p.h. */ + +#define RESERVED_BYTES (64 * 1024) +#define RESERVED_SECTORS (RESERVED_BYTES / 512) + +#define NEW_SIZE_SECTORS(x) ((x & ~(RESERVED_SECTORS - 1)) \ + - RESERVED_SECTORS) + +#define SB_BYTES 4096 +#define SB_WORDS (SB_BYTES / 4) +#define SB_SECTORS (SB_BYTES / 512) + +/* + * The following are counted in 32-bit words + */ +#define SB_GENERIC_OFFSET 0 + +#define SB_PERSONALITY_OFFSET 64 +#define SB_DISKS_OFFSET 128 +#define SB_DESCRIPTOR_OFFSET 992 + +#define SB_GENERIC_CONSTANT_WORDS 32 +#define SB_GENERIC_STATE_WORDS 32 +#define SB_GENERIC_WORDS (SB_GENERIC_CONSTANT_WORDS + \ + SB_GENERIC_STATE_WORDS) + +#define SB_PERSONALITY_WORDS 64 +#define SB_DESCRIPTOR_WORDS 32 +#define SB_DISKS 27 +#define SB_DISKS_WORDS (SB_DISKS * SB_DESCRIPTOR_WORDS) + +#define SB_RESERVED_WORDS (1024 \ + - SB_GENERIC_WORDS \ + - SB_PERSONALITY_WORDS \ + - SB_DISKS_WORDS \ + - SB_DESCRIPTOR_WORDS) + +#define SB_EQUAL_WORDS (SB_GENERIC_WORDS \ + + SB_PERSONALITY_WORDS \ + + SB_DISKS_WORDS) + +/* + * Device "operational" state bits + */ +#define DISK_FAULTY 0 +#define DISK_ACTIVE 1 +#define DISK_SYNC 2 +#define DISK_REMOVED 3 + +#define DISK_WRITEMOSTLY 9 + +#define SB_MAGIC 0xa92b4efc + +/* + * Superblock state bits + */ +#define SB_CLEAN 0 +#define SB_ERRORS 1 + +#define SB_BITMAP_PRESENT 8 + +struct grub_raid_disk_09 +{ + grub_uint32_t number; /* Device number in the entire set. */ + grub_uint32_t major; /* Device major number. */ + grub_uint32_t minor; /* Device minor number. */ + grub_uint32_t raid_disk; /* The role of the device in the raid set. */ + grub_uint32_t state; /* Operational state. */ + grub_uint32_t reserved[SB_DESCRIPTOR_WORDS - 5]; +}; + +struct grub_raid_super_09 +{ + /* + * Constant generic information + */ + grub_uint32_t md_magic; /* MD identifier. */ + grub_uint32_t major_version; /* Major version. */ + grub_uint32_t minor_version; /* Minor version. */ + grub_uint32_t patch_version; /* Patchlevel version. */ + grub_uint32_t gvalid_words; /* Number of used words in this section. */ + grub_uint32_t set_uuid0; /* Raid set identifier. */ + grub_uint32_t ctime; /* Creation time. */ + grub_uint32_t level; /* Raid personality. */ + grub_uint32_t size; /* Apparent size of each individual disk. */ + grub_uint32_t nr_disks; /* Total disks in the raid set. */ + grub_uint32_t raid_disks; /* Disks in a fully functional raid set. */ + grub_uint32_t md_minor; /* Preferred MD minor device number. */ + grub_uint32_t not_persistent; /* Does it have a persistent superblock. */ + grub_uint32_t set_uuid1; /* Raid set identifier #2. */ + grub_uint32_t set_uuid2; /* Raid set identifier #3. */ + grub_uint32_t set_uuid3; /* Raid set identifier #4. */ + grub_uint32_t gstate_creserved[SB_GENERIC_CONSTANT_WORDS - 16]; + + /* + * Generic state information + */ + grub_uint32_t utime; /* Superblock update time. */ + grub_uint32_t state; /* State bits (clean, ...). */ + grub_uint32_t active_disks; /* Number of currently active disks. */ + grub_uint32_t working_disks; /* Number of working disks. */ + grub_uint32_t failed_disks; /* Number of failed disks. */ + grub_uint32_t spare_disks; /* Number of spare disks. */ + grub_uint32_t sb_csum; /* Checksum of the whole superblock. */ + grub_uint64_t events; /* Superblock update count. */ + grub_uint64_t cp_events; /* Checkpoint update count. */ + grub_uint32_t recovery_cp; /* Recovery checkpoint sector count. */ + grub_uint32_t gstate_sreserved[SB_GENERIC_STATE_WORDS - 12]; + + /* + * Personality information + */ + grub_uint32_t layout; /* The array's physical layout. */ + grub_uint32_t chunk_size; /* Chunk size in bytes. */ + grub_uint32_t root_pv; /* LV root PV. */ + grub_uint32_t root_block; /* LV root block. */ + grub_uint32_t pstate_reserved[SB_PERSONALITY_WORDS - 4]; + + /* + * Disks information + */ + struct grub_raid_disk_09 disks[SB_DISKS]; + + /* + * Reserved + */ + grub_uint32_t reserved[SB_RESERVED_WORDS]; + + /* + * Active descriptor + */ + struct grub_raid_disk_09 this_disk; +} __attribute__ ((packed)); + +static grub_err_t +grub_mdraid_detect (grub_disk_t disk, struct grub_raid_array *array) +{ + grub_disk_addr_t sector; + grub_uint64_t size; + struct grub_raid_super_09 sb; + grub_uint32_t *uuid; + + /* The sector where the RAID superblock is stored, if available. */ + size = grub_disk_get_size (disk); + sector = NEW_SIZE_SECTORS (size); + + if (grub_disk_read (disk, sector, 0, SB_BYTES, (char *) &sb)) + return grub_errno; + + /* Look whether there is a RAID superblock. */ + if (sb.md_magic != SB_MAGIC) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "not raid"); + + /* FIXME: Also support version 1.0. */ + if (sb.major_version != 0 || sb.minor_version != 90) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID version: %d.%d", + sb.major_version, sb.minor_version); + + /* FIXME: Check the checksum. */ + + /* Multipath. */ + if ((int) sb.level == -4) + sb.level = 1; + + if (sb.level != 0 && sb.level != 1 && sb.level != 4 && + sb.level != 5 && sb.level != 6 && sb.level != 10) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported RAID level: %d", sb.level); + + array->number = sb.md_minor; + array->level = sb.level; + array->layout = sb.layout; + array->total_devs = sb.raid_disk; + array->disk_size = (sb.size) ? sb.size * 2 : sector; + array->chunk_size = sb.chunk_size >> 9; + array->index = sb.this_disk.number; + array->uuid_len = 16; + array->uuid = grub_malloc (16); + if (!array->uuid) + return grub_errno; + + uuid = (grub_uint32_t *) array->uuid; + uuid[0] = sb.set_uuid0; + uuid[1] = sb.set_uuid1; + uuid[2] = sb.set_uuid2; + uuid[3] = sb.set_uuid3; + + return 0; +} + +static struct grub_raid grub_mdraid_dev = { + .name = "mdraid", + .detect = grub_mdraid_detect, + .next = 0 +}; + +GRUB_MOD_INIT (mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} + +GRUB_MOD_FINI (mdraid) +{ + grub_raid_register (&grub_mdraid_dev); +} diff --git a/disk/raid.c b/disk/raid.c index 7aa2398..50c0671 100644 --- a/disk/raid.c +++ b/disk/raid.c @@ -26,6 +26,8 @@ /* Linked list of RAID arrays. */ static struct grub_raid_array *array_list; +grub_raid5_recover_func_t grub_raid5_recover_func; +grub_raid6_recover_func_t grub_raid6_recover_func; \f static char @@ -43,10 +45,29 @@ grub_is_array_readable (struct grub_raid_array *array) return 1; break; + case 4: case 5: - if (array->nr_devs >= array->total_devs - 1) - return 1; - break; + case 6: + case 10: + { + unsigned int n; + + if (array->level == 10) + { + n = array->layout & 0xFF; + if (n == 1) + n = (array->layout >> 8) & 0xFF; + + n--; + } + else + n = array->level / 3; + + if (array->nr_devs >= array->total_devs - n) + return 1; + + break; + } } return 0; @@ -76,12 +97,13 @@ grub_raid_memberlist (grub_disk_t disk) unsigned int i; for (i = 0; i < array->total_devs; i++) - { - tmp = grub_malloc (sizeof (*tmp)); - tmp->disk = array->device[i]; - tmp->next = list; - list = tmp; - } + if (array->device[i]) + { + tmp = grub_malloc (sizeof (*tmp)); + tmp->disk = array->device[i]; + tmp->next = list; + list = tmp; + } return list; } @@ -91,6 +113,7 @@ static grub_err_t grub_raid_open (const char *name, grub_disk_t disk) { struct grub_raid_array *array; + unsigned n; for (array = array_list; array != NULL; array = array->next) { @@ -100,7 +123,8 @@ grub_raid_open (const char *name, grub_disk_t disk) } if (!array) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s", name); + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "Unknown RAID device %s", + name); disk->has_partitions = 1; disk->id = array->number; @@ -111,17 +135,27 @@ grub_raid_open (const char *name, grub_disk_t disk) switch (array->level) { - case 0: - /* FIXME: RAID0 disks can have different sizes! */ - disk->total_sectors = array->total_devs * array->disk_size; - break; - case 1: disk->total_sectors = array->disk_size; break; + case 10: + n = array->layout & 0xFF; + if (n == 1) + n = (array->layout >> 8) & 0xFF; + + disk->total_sectors = grub_divmod64 (array->total_devs * + array->disk_size, + n, 0); + break; + + case 0: + case 4: case 5: - disk->total_sectors = (array->total_devs - 1) * array->disk_size; + case 6: + n = array->level / 3; + + disk->total_sectors = (array->total_devs - n) * array->disk_size; break; } @@ -137,6 +171,22 @@ grub_raid_close (grub_disk_t disk __attribute ((unused))) return; } +void +grub_raid_block_xor (char *buf1, char *buf2, int size) +{ + grub_size_t *p1, *p2; + + p1 = (grub_size_t *) buf1; + p2 = (grub_size_t *) buf2; + size /= GRUB_CPU_SIZEOF_VOID_P; + + while (size) + { + *(p1++) ^= *(p2++); + size--; + } +} + static grub_err_t grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) @@ -147,190 +197,264 @@ grub_raid_read (grub_disk_t disk, grub_disk_addr_t sector, switch (array->level) { case 0: + case 1: + case 10: { - grub_uint64_t a; - grub_uint32_t b; - unsigned int disknr; - grub_disk_addr_t read_sector; - grub_size_t read_size; - - /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, array->total_devs, &disknr); - - a = grub_divmod64 (sector, array->chunk_size * array->total_devs, NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; - - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; - - while (1) - { - grub_uint32_t i; - - err = grub_disk_read (array->device[disknr], read_sector, 0, - read_size << GRUB_DISK_SECTOR_BITS, buf); - if (err) - break; - - buf += read_size << GRUB_DISK_SECTOR_BITS; + grub_disk_addr_t read_sector, far_ofs; + grub_uint32_t disknr, b, near, far, ofs; + + read_sector = grub_divmod64 (sector, array->chunk_size, &b); + far = ofs = near = 1; + far_ofs = 0; + + if (array->level == 1) + near = array->total_devs; + else if (array->level == 10) + { + near = array->layout & 0xFF; + far = (array->layout >> 8) & 0xFF; + if (array->layout >> 16) + { + ofs = far; + far_ofs = 1; + } + else + far_ofs = grub_divmod64 (array->disk_size, + far * array->chunk_size, 0); + + far_ofs *= array->chunk_size; + } + + read_sector = grub_divmod64 (read_sector * near, array->total_devs, + &disknr); + + ofs *= array->chunk_size; + read_sector *= ofs; + + while (1) + { + grub_size_t read_size; + unsigned int i, j; + + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + for (i = 0; i < near; i++) + { + unsigned int k; + + k = disknr; + for (j = 0; j < far; j++) + { + if (array->device[k]) + { + if (grub_errno == GRUB_ERR_READ_ERROR) + grub_errno = GRUB_ERR_NONE; + + err = grub_disk_read (array->device[k], + read_sector + j * far_ofs + b, + 0, + read_size << GRUB_DISK_SECTOR_BITS, + buf); + if (! err) + break; + else if (err != GRUB_ERR_READ_ERROR) + return err; + } + else + err = grub_error (GRUB_ERR_READ_ERROR, + "disk missing."); + + k++; + if (k == array->total_devs) + k = 0; + } + + if (! err) + break; + + disknr++; + if (disknr == array->total_devs) + { + disknr = 0; + read_sector += ofs; + } + } + + if (err) + return err; + + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - - disknr++; - /* See whether the disk was the last disk, and start - reading from the first disk in that case. */ - if (disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - } - } - } - break; - - case 1: - /* This is easy, we can read from any disk we want. We will loop - over all disks until we've found one that is available. In - case of errs, we will try the to read the next disk. */ - { - unsigned int i = 0; - - for (i = 0; i < array->total_devs; i++) - { - if (array->device[i]) - { - err = grub_disk_read (array->device[i], sector, 0, - size << GRUB_DISK_SECTOR_BITS, buf); - - if (!err) - break; - } - } + b = 0; + disknr += (near - i); + while (disknr >= array->total_devs) + { + disknr -= array->total_devs; + read_sector += ofs; + } + } + break; } - break; + case 4: case 5: + case 6: { - grub_uint64_t a; - grub_uint32_t b; - int disknr; grub_disk_addr_t read_sector; - grub_size_t read_size; - - /* Find the first sector to read. */ - a = grub_divmod64 (sector, array->chunk_size, NULL); - grub_divmod64 (a, (array->total_devs - 1), &b); - disknr = b; + grub_uint32_t b, p, n, disknr, e; - a = grub_divmod64 (sector, array->chunk_size * (array->total_devs - 1), - NULL); - grub_divmod64 (sector, array->chunk_size, &b); - read_sector = a * array->chunk_size + b; + /* n = 1 for level 4 and 5, 2 for level 6. */ + n = array->level / 3; - grub_divmod64 (read_sector, array->chunk_size * array->total_devs, &b); - disknr -= (b / array->chunk_size); - if (disknr < 0) - disknr += array->total_devs; - - grub_divmod64 (read_sector, array->chunk_size, &b); - read_size = array->chunk_size - b; - - if (read_size > size) - read_size = size; + /* Find the first sector to read. */ + read_sector = grub_divmod64 (sector, array->chunk_size, &b); + read_sector = grub_divmod64 (read_sector, array->total_devs - n, + &disknr); + if (array->level >= 5) + { + grub_divmod64 (read_sector, array->total_devs, &p); + + if (! (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK)) + p = array->total_devs - 1 - p; + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr += p + n; + } + else + { + grub_uint32_t q; + + q = p + (n - 1); + if (q >= array->total_devs) + q -= array->total_devs; + + if (disknr >= p) + disknr += n; + else if (disknr >= q) + disknr += q + 1; + } + + if (disknr >= array->total_devs) + disknr -= array->total_devs; + } + else + p = array->total_devs - n; + + read_sector *= array->chunk_size; while (1) { - grub_uint32_t i; - - if (array->device[disknr]) - err = grub_disk_read (array->device[disknr], read_sector, 0, - read_size << GRUB_DISK_SECTOR_BITS, buf); + grub_size_t read_size; + int next_level; + + read_size = array->chunk_size - b; + if (read_size > size) + read_size = size; + + e = 0; + if (array->device[disknr]) + { + /* Reset read error. */ + if (grub_errno == GRUB_ERR_READ_ERROR) + grub_errno = GRUB_ERR_NONE; + + err = grub_disk_read (array->device[disknr], + read_sector + b, 0, + read_size << GRUB_DISK_SECTOR_BITS, + buf); + + if ((err) && (err != GRUB_ERR_READ_ERROR)) + break; + e++; + } + else + err = GRUB_ERR_READ_ERROR; - /* If an error occurs when we already have an degraded - array we can't recover from that. */ - if (err && ((array->total_devs - 1) == array->nr_devs)) - break; + if (err) + { + if (array->nr_devs < array->total_devs - n + e) + break; + + grub_errno = GRUB_ERR_NONE; + if (array->level == 6) + { + err = ((grub_raid6_recover_func) ? + (*grub_raid6_recover_func) (array, disknr, p, + buf, read_sector + b, + read_size) : + grub_error (GRUB_ERR_BAD_DEVICE, + "raid6rec is not loaded")); + } + else + { + err = ((grub_raid5_recover_func) ? + (*grub_raid5_recover_func) (array, disknr, + buf, read_sector + b, + read_size) : + grub_error (GRUB_ERR_BAD_DEVICE, + "raid5rec is not loaded")); + } + + if (err) + break; + } - if (err || ! array->device[disknr]) - { - /* Either an error occured or the disk is not - available. We have to compute this block from the - blocks on the other hard disks. */ - grub_size_t buf_size = read_size << GRUB_DISK_SECTOR_BITS; - char buf2[buf_size]; - unsigned int j; - - grub_memset (buf, 0, buf_size); - - for (j = 0; j < array->total_devs; j++) - { - unsigned int k; - - if (j != (unsigned int) disknr) - { - err = grub_disk_read (array->device[j], read_sector, - 0, buf_size, buf2); - if (err) - return err; - - for (k = 0; k < buf_size; k++) - buf[k] = buf[k] ^ buf2[k]; - } - } - } - - buf += (read_size << GRUB_DISK_SECTOR_BITS); + buf += read_size << GRUB_DISK_SECTOR_BITS; size -= read_size; if (! size) break; - if (size > array->chunk_size) - read_size = array->chunk_size; - else - read_size = size; - - /* Check whether the sector was aligned on a chunk size - boundary. If this isn't the case, it's the first read - and the next read should be set back to start of the - boundary. */ - grub_divmod64 (read_sector, array->chunk_size, &i); - read_sector -= i; - + b = 0; disknr++; - grub_divmod64 (read_sector, - array->chunk_size * array->total_devs, &i); - if ((unsigned int) disknr == (array->total_devs - (i / array->chunk_size) - 1)) - disknr++; - /* See whether the disk was the last disk, and start - reading from the first disk in that case. */ - if ((unsigned int) disknr == array->total_devs) - { - disknr = 0; - read_sector += array->chunk_size; - grub_divmod64 (read_sector, - array->chunk_size * array->total_devs, &i); - - if ((i / array->chunk_size) == (array->total_devs - 1)) - disknr++; - } + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + if (disknr == array->total_devs) + disknr = 0; + + next_level = (disknr == p); + } + else + { + if (disknr == p) + disknr += n; + + next_level = (disknr >= array->total_devs); + } + + if (next_level) + { + read_sector += array->chunk_size; + + if (array->level >= 5) + { + if (array->layout & GRUB_RAID_LAYOUT_RIGHT_MASK) + p = (p == array->total_devs - 1) ? 0 : p + 1; + else + p = (p == 0) ? array->total_devs - 1 : p - 1; + + if (array->layout & GRUB_RAID_LAYOUT_SYMMETRIC_MASK) + { + disknr = p + n; + if (disknr >= array->total_devs) + disknr -= array->total_devs; + } + else + { + disknr -= array->total_devs; + if (disknr == p) + disknr += n; + } + } + else + disknr = 0; + } } } break; @@ -348,169 +472,105 @@ grub_raid_write (grub_disk_t disk __attribute ((unused)), return GRUB_ERR_NOT_IMPLEMENTED_YET; } -static int -grub_raid_scan_device (const char *name) +static grub_err_t +insert_array (grub_disk_t disk, struct grub_raid_array *new_array, + const char *scanner_name) { - grub_err_t err; - grub_disk_t disk; - grub_disk_addr_t sector; - grub_uint64_t size; - struct grub_raid_super_09 sb; - struct grub_raid_array *p, *array = NULL; - - grub_dprintf ("raid", "Scanning for RAID devices\n"); - - disk = grub_disk_open (name); - if (!disk) - return 0; - - /* The sector where the RAID superblock is stored, if available. */ - size = grub_disk_get_size (disk); - sector = GRUB_RAID_NEW_SIZE_SECTORS(size); - - err = grub_disk_read (disk, sector, 0, GRUB_RAID_SB_BYTES, (char *) &sb); - grub_disk_close (disk); - if (err) - { - grub_errno = GRUB_ERR_NONE; - return 0; - } - - /* Look whether there is a RAID superblock. */ - if (sb.md_magic != GRUB_RAID_SB_MAGIC) - return 0; - - /* FIXME: Also support version 1.0. */ - if (sb.major_version != 0 || sb.minor_version != 90) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID version: %d.%d", - sb.major_version, sb.minor_version); - return 0; - } - - /* FIXME: Check the checksum. */ - - /* FIXME: Support all RAID levels. */ - if (sb.level != 0 && sb.level != 1 && sb.level != 5) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID level: %d", - sb.level); - return 0; - } - - /* FIXME: Support all layouts. */ - if (sb.level == 5 && sb.layout != 2) - { - grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "Unsupported RAID5 layout: %d", - sb.layout); - return 0; - } + struct grub_raid_array *array = 0, *p; /* See whether the device is part of an array we have already seen a device from. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->uuid[0] == sb.set_uuid0 && p->uuid[1] == sb.set_uuid1 - && p->uuid[2] == sb.set_uuid2 && p->uuid[3] == sb.set_uuid3) - { - array = p; - break; - } - } - - /* Do some checks before adding the device to the array. */ - if (array) - { - /* FIXME: Check whether the update time of the superblocks are - the same. */ - - if (array->total_devs == array->nr_devs) - { - /* We found more members of the array than the array - actually has according to its superblock. This shouldn't - happen normally, but what is the sanest things to do in such - a case? */ - - grub_error (GRUB_ERR_BAD_NUMBER, - "array->nr_devs > array->total_devs (%d)?!?", - array->total_devs); - - return 0; - } - - if (array->device[sb.this_disk.number] != NULL) - /* We found multiple devices with the same number. Again, - this shouldn't happen.*/ - grub_dprintf ("raid", "Found two disks with the number %d?!?", - sb.this_disk.number); - } + if ((p->uuid_len == new_array->uuid_len) && + (! grub_memcmp (p->uuid, new_array->uuid, p->uuid_len))) + { + grub_free (new_array->uuid); + array = p; + + /* Do some checks before adding the device to the array. */ + + /* FIXME: Check whether the update time of the superblocks are + the same. */ + + if (array->total_devs == array->nr_devs) + /* We found more members of the array than the array + actually has according to its superblock. This shouldn't + happen normally, but what is the sanest things to do in such + a case? */ + return grub_error (GRUB_ERR_BAD_NUMBER, + "array->nr_devs > array->total_devs (%d)?!?", + array->total_devs); + + if (array->device[new_array->index] != NULL) + /* We found multiple devices with the same number. Again, + this shouldn't happen.*/ + return grub_error (GRUB_ERR_BAD_NUMBER, + "Found two disks with the number %d?!?", + new_array->number); + + if (new_array->disk_size < array->disk_size) + array->disk_size = new_array->disk_size; + break; + } /* Add an array to the list if we didn't find any. */ if (!array) { array = grub_malloc (sizeof (*array)); if (!array) - return 0; - grub_memset (array, 0, sizeof (*array)); - array->number = sb.md_minor; - array->version = sb.major_version; - array->level = sb.level; - array->layout = sb.layout; - array->total_devs = sb.nr_disks; + { + grub_free (new_array->uuid); + return grub_errno; + } + + *array = *new_array; array->nr_devs = 0; - array->uuid[0] = sb.set_uuid0; - array->uuid[1] = sb.set_uuid1; - array->uuid[2] = sb.set_uuid2; - array->uuid[3] = sb.set_uuid3; - /* The superblock specifies the size in 1024-byte sectors. */ - array->disk_size = sb.size * 2; - array->chunk_size = sb.chunk_size / 512; + grub_memset (&array->device, 0, sizeof (array->device)); /* Check whether we don't have multiple arrays with the same number. */ for (p = array_list; p != NULL; p = p->next) - { - if (p->number == array->number) - break; - } + { + if (p->number == array->number) + break; + } if (p) - { - /* The number is already in use, so we need to find an new number. */ - int i = 0; - - while (1) - { - for (p = array_list; p != NULL; p = p->next) - { - if (p->number == i) - break; - } - - if (!p) - { - /* We found an unused number. */ - array->number = i; - break; - } - - i++; - } - } + { + /* The number is already in use, so we need to find an new number. */ + int i = 0; + + while (1) + { + for (p = array_list; p != NULL; p = p->next) + { + if (p->number == i) + break; + } + + if (!p) + { + /* We found an unused number. */ + array->number = i; + break; + } + + i++; + } + } array->name = grub_malloc (13); if (! array->name) - { - grub_free (array); + { + grub_free (array->uuid); + grub_free (array); - return 0; - } + return grub_errno; + } grub_sprintf (array->name, "md%d", array->number); - grub_dprintf ("raid", "Found array: %s\n", array->name); + grub_dprintf ("raid", "Found array %s (%s)\n", array->name, + scanner_name); /* Add our new array to the list. */ array->next = array_list; @@ -518,47 +578,116 @@ grub_raid_scan_device (const char *name) } /* Add the device to the array. */ - array->device[sb.this_disk.number] = grub_disk_open (name); + array->device[new_array->index] = disk; + array->nr_devs++; + + return 0; +} - if (array->disk_size != array->device[sb.this_disk.number]->total_sectors) +static grub_raid_t grub_raid_list; + +static void +grub_raid_scan_device (int head_only) +{ + auto int hook (const char *name); + int hook (const char *name) { - if (array->total_devs == 1) - { - grub_dprintf ("raid", "Array contains only one disk, but its size (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } - else if (array->level == 1) - { - grub_dprintf ("raid", "Array is RAID level 1, but the size of disk %d (0x%llx) " - "doesn't match with size indicated by superblock (0x%llx). " - "Assuming superblock is wrong.\n", - sb.this_disk.number, - (unsigned long long) array->device[sb.this_disk.number]->total_sectors, - (unsigned long long) array->disk_size); - array->disk_size = array->device[sb.this_disk.number]->total_sectors; - } + grub_disk_t disk; + struct grub_raid_array array; + struct grub_raid *p; + + grub_dprintf ("raid", "Scanning for RAID devices\n"); + + disk = grub_disk_open (name); + if (!disk) + return 0; + + if (disk->total_sectors == ULONG_MAX) + { + grub_disk_close (disk); + return 0; + } + + for (p = grub_raid_list; p; p = p->next) + { + if (! p->detect (disk, &array)) + { + if (! insert_array (disk, &array, p->name)) + return 0; + + break; + } + + /* This error usually means it's not raid, no need to display + it. */ + if (grub_errno != GRUB_ERR_OUT_OF_RANGE) + grub_print_error (); + + grub_errno = GRUB_ERR_NONE; + if (head_only) + break; + } + + grub_disk_close (disk); + + return 0; } - if (! array->device[sb.this_disk.number]) + grub_device_iterate (&hook); +} + +static void +free_array (void) +{ + struct grub_raid_array *array; + + array = array_list; + while (array) { - /* Remove array from the list if we have just added it. */ - if (array->nr_devs == 0) - { - array_list = array->next; - grub_free (array->name); - grub_free (array); - } + struct grub_raid_array *p; + int i; - return 0; + p = array; + array = array->next; + + for (i = 0; i < GRUB_RAID_MAX_DEVICES; i++) + if (p->device[i]) + grub_disk_close (p->device[i]); + + grub_free (p->uuid); + grub_free (p->name); + grub_free (p); } - array->nr_devs++; + array_list = 0; +} - return 0; +void +grub_raid_register (grub_raid_t raid) +{ + raid->next = grub_raid_list; + grub_raid_list = raid; + grub_raid_scan_device (1); +} + +void +grub_raid_unregister (grub_raid_t raid) +{ + grub_raid_t *p, q; + + for (p = &grub_raid_list, q = *p; q; p = &(q->next), q = q->next) + if (q == raid) + { + *p = q->next; + break; + } +} + +void +grub_raid_rescan (void) +{ + free_array (); + grub_raid_scan_device (0); } static struct grub_disk_dev grub_raid_dev = @@ -579,18 +708,11 @@ static struct grub_disk_dev grub_raid_dev = \f GRUB_MOD_INIT(raid) { - grub_device_iterate (&grub_raid_scan_device); - if (grub_errno) - { - grub_print_error (); - grub_errno = GRUB_ERR_NONE; - } - grub_disk_dev_register (&grub_raid_dev); } GRUB_MOD_FINI(raid) { grub_disk_dev_unregister (&grub_raid_dev); - /* FIXME: free the array list. */ + free_array (); } diff --git a/disk/raid5_recover.c b/disk/raid5_recover.c new file mode 100644 index 0000000..31cef88 --- /dev/null +++ b/disk/raid5_recover.c @@ -0,0 +1,72 @@ +/* raid5_recover.c - module to recover from faulty RAID4/5 arrays. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +static grub_err_t +grub_raid5_recover (struct grub_raid_array *array, int disknr, + char *buf, grub_disk_addr_t sector, int size) +{ + char *buf2; + int i; + + size <<= GRUB_DISK_SECTOR_BITS; + buf2 = grub_malloc (size); + if (!buf2) + return grub_errno; + + grub_memset (buf, 0, size); + + for (i = 0; i < (int) array->total_devs; i++) + { + grub_err_t err; + + if (i == disknr) + continue; + + err = grub_disk_read (array->device[i], sector, 0, size, buf2); + + if (err) + { + grub_free (buf2); + return err; + } + + grub_raid_block_xor (buf, buf2, size); + } + + grub_free (buf2); + + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT(raid5rec) +{ + grub_raid5_recover_func = grub_raid5_recover; +} + +GRUB_MOD_FINI(raid5rec) +{ + grub_raid5_recover_func = 0; +} diff --git a/disk/raid6_recover.c b/disk/raid6_recover.c new file mode 100644 index 0000000..3cb08ab --- /dev/null +++ b/disk/raid6_recover.c @@ -0,0 +1,216 @@ +/* raid6_recover.c - module to recover from faulty RAID6 arrays. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 <http://www.gnu.org/licenses/>. + */ + +#include <grub/dl.h> +#include <grub/disk.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/raid.h> + +static grub_uint8_t raid6_table1[256][256]; +static grub_uint8_t raid6_table2[256][256]; + +static void +grub_raid_block_mul (grub_uint8_t mul, char *buf, int size) +{ + int i; + grub_uint8_t *p; + + p = (grub_uint8_t *) buf; + for (i = 0; i < size; i++, p++) + *p = raid6_table1[mul][*p]; +} + +static void +grub_raid6_init_table (void) +{ + int i, j; + + for (i = 0; i < 256; i++) + raid6_table1[i][1] = raid6_table1[1][i] = i; + + for (i = 2; i < 256; i++) + for (j = i; j < 256; j++) + { + int n; + grub_uint8_t c; + + n = i >> 1; + + c = raid6_table1[n][j]; + c = (c << 1) ^ ((c & 0x80) ? 0x1d : 0); + if (i & 1) + c ^= j; + + raid6_table1[j][i] = raid6_table1[i][j] = c; + } + + raid6_table2[0][0] = 1; + for (i = 1; i < 256; i++) + raid6_table2[i][i] = raid6_table1[raid6_table2[i - 1][i - 1]][2]; + + for (i = 0; i < 254; i++) + for (j = 0; j < 254; j++) + { + grub_uint8_t c, n; + int k; + + if (i == j) + continue; + + k = i - j; + if (k < 0) + k += 255; + + c = n = raid6_table2[k][k] ^ 1; + for (k = 0; k < 253; k++) + c = raid6_table1[c][n]; + + raid6_table2[i][j] = raid6_table1[raid6_table2[255 - j][255 - j]][c]; + } +} + +static grub_err_t +grub_raid6_recover (struct grub_raid_array *array, int disknr, int p, + char *buf, grub_disk_addr_t sector, int size) +{ + int i, q, pos; + int err[2], nerr; + char *pbuf = 0, *qbuf = 0; + + size <<= GRUB_DISK_SECTOR_BITS; + pbuf = grub_malloc (size); + if (!pbuf) + goto quit; + + qbuf = grub_malloc (size); + if (!qbuf) + goto quit; + + q = p + 1; + if (q == (int) array->total_devs) + q = 0; + + grub_memset (pbuf, 0, size); + grub_memset (qbuf, 0, size); + + pos = q + 1; + if (pos == (int) array->total_devs) + pos = 0; + + nerr = 1; + for (i = 0; i < (int) array->total_devs - 2; i++) + { + if (pos == disknr) + err[0] = i; + else + { + if ((array->device[pos]) && + (! grub_disk_read (array->device[pos], sector, 0, size, buf))) + { + grub_raid_block_xor (pbuf, buf, size); + grub_raid_block_mul (raid6_table2[i][i], buf, size); + grub_raid_block_xor (qbuf, buf, size); + } + else + { + if (nerr >= 2) + goto quit; + + err[nerr++] = i; + grub_errno = GRUB_ERR_NONE; + } + } + + pos++; + if (pos == (int) array->total_devs) + pos = 0; + } + + if (nerr == 1) + { + if ((array->device[p]) && + (! grub_disk_read (array->device[p], sector, 0, size, buf))) + { + grub_raid_block_xor (buf, pbuf, size); + goto quit; + } + + if (! array->device[q]) + { + grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore"); + goto quit; + } + + grub_errno = GRUB_ERR_NONE; + if (grub_disk_read (array->device[q], sector, 0, size, buf)) + goto quit; + + grub_raid_block_xor (buf, qbuf, size); + grub_raid_block_mul (raid6_table2[255 - err[0]][255 - err[0]], buf, + size); + } + else + { + grub_uint8_t c; + + if ((! array->device[p]) || (! array->device[q])) + { + grub_error (GRUB_ERR_READ_ERROR, "Not enough disk to restore"); + goto quit; + } + + if (grub_disk_read (array->device[p], sector, 0, size, buf)) + goto quit; + + grub_raid_block_xor (pbuf, buf, size); + + if (grub_disk_read (array->device[q], sector, 0, size, buf)) + goto quit; + + grub_raid_block_xor (qbuf, buf, size); + + c = raid6_table2[err[1]][err[0]]; + grub_raid_block_mul (c, qbuf, size); + + c = raid6_table1[raid6_table2[err[1]][err[1]]][c]; + grub_raid_block_mul (c, pbuf, size); + + grub_raid_block_xor (pbuf, qbuf, size); + grub_memcpy (buf, pbuf, size); + } + +quit: + grub_free (pbuf); + grub_free (qbuf); + + return grub_errno; +} + +GRUB_MOD_INIT(raid6rec) +{ + grub_raid6_init_table (); + grub_raid6_recover_func = grub_raid6_recover; +} + +GRUB_MOD_FINI(raid6rec) +{ + grub_raid6_recover_func = 0; +} diff --git a/include/grub/raid.h b/include/grub/raid.h index 4af97f1..a36be6d 100644 --- a/include/grub/raid.h +++ b/include/grub/raid.h @@ -22,165 +22,65 @@ #include <grub/types.h> +#define GRUB_RAID_MAX_DEVICES 32 + +#define GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC 0 +#define GRUB_RAID_LAYOUT_RIGHT_ASYMMETRIC 1 +#define GRUB_RAID_LAYOUT_LEFT_SYMMETRIC 2 +#define GRUB_RAID_LAYOUT_RIGHT_SYMMETRIC 3 + +#define GRUB_RAID_LAYOUT_RIGHT_MASK 1 +#define GRUB_RAID_LAYOUT_SYMMETRIC_MASK 2 + struct grub_raid_array { int number; /* The device number, taken from md_minor so we are consistent with the device name in Linux. */ - int version; /* 0 = 0.90, 1 = 1.0 */ int level; /* RAID levels, only 0, 1 or 5 at the moment. */ - int layout; /* Only for RAID 5. */ + int layout; /* Layout for RAID 5/6. */ unsigned int total_devs; /* Total number of devices in the array. */ - unsigned int nr_devs; /* The number of devices we've found so far. */ - grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ - grub_uint32_t uuid[4]; /* The UUID of the device. */ - char *name; /* That will be "md<number>". */ + grub_size_t chunk_size; /* The size of a chunk, in 512 byte sectors. */ grub_uint64_t disk_size; /* Size of an individual disk, in 512 byte sectors. */ - grub_disk_t device[32]; /* Array of total_devs devices. */ + int index; /* Index of current device. */ + int uuid_len; /* The length of uuid. */ + char *uuid; /* The UUID of the device. */ + + /* The following field is setup by the caller. */ + char *name; /* That will be "md<number>". */ + unsigned int nr_devs; /* The number of devices we've found so far. */ + grub_disk_t device[GRUB_RAID_MAX_DEVICES]; /* Array of total_devs devices. */ struct grub_raid_array *next; }; -/* Linux RAID on disk structures and constants, - copied from include/linux/raid/md_p.h. */ - -#define GRUB_RAID_RESERVED_BYTES (64 * 1024) -#define GRUB_RAID_RESERVED_SECTORS (GRUB_RAID_RESERVED_BYTES / 512) - -#define GRUB_RAID_NEW_SIZE_SECTORS(x) ((x & ~(GRUB_RAID_RESERVED_SECTORS - 1)) \ - - GRUB_RAID_RESERVED_SECTORS) - -#define GRUB_RAID_SB_BYTES 4096 -#define GRUB_RAID_SB_WORDS (GRUB_RAID_SB_BYTES / 4) -#define GRUB_RAID_SB_SECTORS (GRUB_RAID_SB_BYTES / 512) +struct grub_raid +{ + const char *name; -/* - * The following are counted in 32-bit words - */ -#define GRUB_RAID_SB_GENERIC_OFFSET 0 - -#define GRUB_RAID_SB_PERSONALITY_OFFSET 64 -#define GRUB_RAID_SB_DISKS_OFFSET 128 -#define GRUB_RAID_SB_DESCRIPTOR_OFFSET 992 - -#define GRUB_RAID_SB_GENERIC_CONSTANT_WORDS 32 -#define GRUB_RAID_SB_GENERIC_STATE_WORDS 32 -#define GRUB_RAID_SB_GENERIC_WORDS (GRUB_RAID_SB_GENERIC_CONSTANT_WORDS \ - + GRUB_RAID_SB_GENERIC_STATE_WORDS) -#define GRUB_RAID_SB_PERSONALITY_WORDS 64 -#define GRUB_RAID_SB_DESCRIPTOR_WORDS 32 -#define GRUB_RAID_SB_DISKS 27 -#define GRUB_RAID_SB_DISKS_WORDS (GRUB_RAID_SB_DISKS*GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_RESERVED_WORDS (1024 - GRUB_RAID_SB_GENERIC_WORDS \ - - GRUB_RAID_SB_PERSONALITY_WORDS \ - - GRUB_RAID_SB_DISKS_WORDS \ - - GRUB_RAID_SB_DESCRIPTOR_WORDS) -#define GRUB_RAID_SB_EQUAL_WORDS (GRUB_RAID_SB_GENERIC_WORDS \ - + GRUB_RAID_SB_PERSONALITY_WORDS \ - + GRUB_RAID_SB_DISKS_WORDS) + grub_err_t (*detect) (grub_disk_t disk, struct grub_raid_array *array); -/* - * Device "operational" state bits - */ -#define GRUB_RAID_DISK_FAULTY 0 /* disk is faulty / operational */ -#define GRUB_RAID_DISK_ACTIVE 1 /* disk is running or spare disk */ -#define GRUB_RAID_DISK_SYNC 2 /* disk is in sync with the raid set */ -#define GRUB_RAID_DISK_REMOVED 3 /* disk is in sync with the raid set */ + struct grub_raid *next; +}; +typedef struct grub_raid *grub_raid_t; -#define GRUB_RAID_DISK_WRITEMOSTLY 9 /* disk is "write-mostly" is RAID1 config. - * read requests will only be sent here in - * dire need - */ +void grub_raid_register (grub_raid_t raid); +void grub_raid_unregister (grub_raid_t raid); +void grub_raid_rescan (void); +void grub_raid_block_xor (char *buf1, char *buf2, int size); -#define GRUB_RAID_SB_MAGIC 0xa92b4efc +typedef grub_err_t (*grub_raid5_recover_func_t) (struct grub_raid_array *array, + int disknr, char *buf, + grub_disk_addr_t sector, + int size); -/* - * Superblock state bits - */ -#define GRUB_RAID_SB_CLEAN 0 -#define GRUB_RAID_SB_ERRORS 1 - -#define GRUB_RAID_SB_BITMAP_PRESENT 8 /* bitmap may be present nearby */ - -struct grub_raid_disk_09 { - grub_uint32_t number; /* 0 Device number in the entire set */ - grub_uint32_t major; /* 1 Device major number */ - grub_uint32_t minor; /* 2 Device minor number */ - grub_uint32_t raid_disk; /* 3 The role of the device in the raid set */ - grub_uint32_t state; /* 4 Operational state */ - grub_uint32_t reserved[GRUB_RAID_SB_DESCRIPTOR_WORDS - 5]; -}; +typedef grub_err_t (*grub_raid6_recover_func_t) (struct grub_raid_array *array, + int disknr, int p, char *buf, + grub_disk_addr_t sector, + int size); -struct grub_raid_super_09 { - /* - * Constant generic information - */ - grub_uint32_t md_magic; /* 0 MD identifier */ - grub_uint32_t major_version; /* 1 major version to which the set conforms */ - grub_uint32_t minor_version; /* 2 minor version ... */ - grub_uint32_t patch_version; /* 3 patchlevel version ... */ - grub_uint32_t gvalid_words; /* 4 Number of used words in this section */ - grub_uint32_t set_uuid0; /* 5 Raid set identifier */ - grub_uint32_t ctime; /* 6 Creation time */ - grub_uint32_t level; /* 7 Raid personality */ - grub_uint32_t size; /* 8 Apparent size of each individual disk */ - grub_uint32_t nr_disks; /* 9 total disks in the raid set */ - grub_uint32_t raid_disks; /* 10 disks in a fully functional raid set */ - grub_uint32_t md_minor; /* 11 preferred MD minor device number */ - grub_uint32_t not_persistent; /* 12 does it have a persistent superblock */ - grub_uint32_t set_uuid1; /* 13 Raid set identifier #2 */ - grub_uint32_t set_uuid2; /* 14 Raid set identifier #3 */ - grub_uint32_t set_uuid3; /* 15 Raid set identifier #4 */ - grub_uint32_t gstate_creserved[GRUB_RAID_SB_GENERIC_CONSTANT_WORDS - 16]; - - /* - * Generic state information - */ - grub_uint32_t utime; /* 0 Superblock update time */ - grub_uint32_t state; /* 1 State bits (clean, ...) */ - grub_uint32_t active_disks; /* 2 Number of currently active disks */ - grub_uint32_t working_disks; /* 3 Number of working disks */ - grub_uint32_t failed_disks; /* 4 Number of failed disks */ - grub_uint32_t spare_disks; /* 5 Number of spare disks */ - grub_uint32_t sb_csum; /* 6 checksum of the whole superblock */ -#ifdef GRUB_HOST_WORDS_BIGENDIAN - grub_uint32_t events_hi; /* 7 high-order of superblock update count */ - grub_uint32_t events_lo; /* 8 low-order of superblock update count */ - grub_uint32_t cp_events_hi; /* 9 high-order of checkpoint update count */ - grub_uint32_t cp_events_lo; /* 10 low-order of checkpoint update count */ -#else - grub_uint32_t events_lo; /* 7 low-order of superblock update count */ - grub_uint32_t events_hi; /* 8 high-order of superblock update count */ - grub_uint32_t cp_events_lo; /* 9 low-order of checkpoint update count */ - grub_uint32_t cp_events_hi; /* 10 high-order of checkpoint update count */ -#endif - grub_uint32_t recovery_cp; /* 11 recovery checkpoint sector count */ - grub_uint32_t gstate_sreserved[GRUB_RAID_SB_GENERIC_STATE_WORDS - 12]; - - /* - * Personality information - */ - grub_uint32_t layout; /* 0 the array's physical layout */ - grub_uint32_t chunk_size; /* 1 chunk size in bytes */ - grub_uint32_t root_pv; /* 2 LV root PV */ - grub_uint32_t root_block; /* 3 LV root block */ - grub_uint32_t pstate_reserved[GRUB_RAID_SB_PERSONALITY_WORDS - 4]; - - /* - * Disks information - */ - struct grub_raid_disk_09 disks[GRUB_RAID_SB_DISKS]; - - /* - * Reserved - */ - grub_uint32_t reserved[GRUB_RAID_SB_RESERVED_WORDS]; - - /* - * Active descriptor - */ - struct grub_raid_disk_09 this_disk; -}; +extern grub_raid5_recover_func_t grub_raid5_recover_func; +extern grub_raid6_recover_func_t grub_raid6_recover_func; #endif /* ! GRUB_RAID_H */ diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 35af6a5..a1c3c89 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -29,7 +29,9 @@ #include <grub/term.h> #include <grub/mm.h> #include <grub/normal.h> +#include <grub/raid.h> #include <grub/lib/hexdump.h> +#include <grub/lib/crc.h> #include <grub_fstest_init.h> @@ -136,12 +138,12 @@ grub_unregister_command (const char *name __attribute__ ((unused))) #define CMD_CP 2 #define CMD_CMP 3 #define CMD_HEX 4 -#define CMD_BLOCKLIST 5 +#define CMD_CRC 6 +#define CMD_BLOCKLIST 7 #define BUF_SIZE 32256 static grub_off_t skip, leng; -static char *part; static void read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) @@ -150,16 +152,53 @@ read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) grub_file_t file; grub_off_t ofs, len; + if ((pathname[0] == '-') && (pathname[1] == 0)) + { + grub_device_t dev; + + dev = grub_device_open (0); + if ((! dev) || (! dev->disk)) + grub_util_error ("Can\'t open device."); + + grub_util_info ("total sectors : %lld.", + (unsigned long long) dev->disk->total_sectors); + + if (! leng) + leng = (dev->disk->total_sectors << GRUB_DISK_SECTOR_BITS) - skip; + + while (leng) + { + grub_size_t len; + + len = (leng > BUF_SIZE) ? BUF_SIZE : leng; + + if (grub_disk_read (dev->disk, 0, skip, len, buf)) + grub_util_error ("Disk read fails at offset %lld, length %d.", + skip, len); + + if (hook (skip, buf, len)) + break; + + skip += len; + leng -= len; + } + + grub_device_close (dev); + return; + } + file = grub_file_open (pathname); if (!file) { - grub_util_error ("cannot open file %s.\n", pathname); + grub_util_error ("cannot open file %s.", pathname); return; } + grub_util_info ("file size : %lld.", (unsigned long long) file->size); + if (skip > file->size) { - grub_util_error ("invalid skip value %d.\n"); + grub_util_error ("invalid skip value %d."); return; } @@ -177,7 +216,7 @@ read_file (char *pathname, int (*hook) (grub_off_t ofs, char *buf, int len)) sz = grub_file_read (file, buf, (len > BUF_SIZE) ? BUF_SIZE : len); if (sz < 0) { - grub_util_error ("read error at offset %llu.\n", ofs); + grub_util_error ("read error at offset %llu.", ofs); break; } @@ -203,7 +242,7 @@ cmd_cp (char *src, char *dest) if ((int) fwrite (buf, 1, len, ff) != len) { - grub_util_error ("write error.\n"); + grub_util_error ("write error."); return 1; } @@ -213,7 +252,7 @@ cmd_cp (char *src, char *dest) ff = fopen (dest, "w"); if (ff == NULL) { - grub_util_error ("open error.\n"); + grub_util_error ("open error."); return; } read_file (src, cp_hook); @@ -231,7 +270,7 @@ cmd_cmp (char *src, char *dest) { if ((int) fread (buf_1, 1, len, ff) != len) { - grub_util_error ("read error at offset %llu.\n", ofs); + grub_util_error ("read error at offset %llu.", ofs); return 1; } @@ -242,7 +281,7 @@ cmd_cmp (char *src, char *dest) for (i = 0; i < len; i++, ofs++) if (buf_1[i] != buf[i]) { - grub_util_error ("compare fail at offset %llu.\n", ofs); + grub_util_error ("compare fail at offset %llu.", ofs); return 1; } } @@ -252,12 +291,12 @@ cmd_cmp (char *src, char *dest) ff = fopen (dest, "r"); if (ff == NULL) { - grub_util_error ("open error.\n"); + grub_util_error ("open error."); return; } if ((skip) && (fseeko (ff, skip, SEEK_SET))) - grub_util_error ("seek error.\n"); + grub_util_error ("seek error."); read_file (src, cmp_hook); fclose (ff); @@ -277,28 +316,44 @@ cmd_hex (char *pathname) } static void -fstest (char *image_path, int cmd, int n, char **args) +cmd_crc (char *pathname) { - char host_file[7 + grub_strlen (image_path) + 1]; - char device_name[(part) ? (6 + grub_strlen (part)) : 5]; - char *argv[3] = { "-p", "loop", host_file }; + grub_uint32_t crc = 0; + + auto int crc_hook (grub_off_t ofs, char *buf, int len); + int crc_hook (grub_off_t ofs, char *buf, int len) + { + (void) ofs; + + crc = grub_getcrc32 (crc, buf, len); + return 0; + } + read_file (pathname, crc_hook); + printf ("%08x\n", crc); +} - grub_sprintf (host_file, "(host)/%s", image_path); +static void +fstest (char **images, int num_disks, int cmd, int n, char **args) +{ + char host_file[128]; + char loop_name[8]; + char *argv[3] = { "-p", loop_name, host_file}; + int i; - if (execute_command (&cmd_loopback, 3, argv)) + for (i = 0; i < num_disks; i++) { - grub_util_error ("loopback command fails.\n"); - goto fail; - } + if (grub_strlen (images[i]) + 7 > sizeof (host_file)) + grub_util_error ("Pathname %s too long.", images[i]); - if (part) - grub_sprintf (device_name, "loop,%s", part); - else - grub_strcpy (device_name, "loop"); + grub_sprintf (loop_name, "loop%d", i); + grub_sprintf (host_file, "(host)%s", images[i]); - grub_env_set ("root", device_name); + if (execute_command (&cmd_loopback, 3, argv)) + grub_util_error ("loopback command fails."); + } + grub_raid_rescan (); switch (cmd) { case CMD_LS: @@ -313,29 +368,32 @@ fstest (char *image_path, int cmd, int n, char **args) case CMD_HEX: cmd_hex (args[0]); break; + case CMD_CRC: + cmd_crc (args[0]); + break; case CMD_BLOCKLIST: execute_command (&cmd_blocklist, n, args); grub_printf ("\n"); } -fail: - argv[0] = "-d"; - execute_command (&cmd_loopback, 2, argv); + for (i = 0; i < num_disks; i++) + { + grub_sprintf (loop_name, "loop%d", i); + execute_command (&cmd_loopback, 2, argv); + } } static struct option options[] = { - {"part", required_argument, 0, 'p'}, + {"root", required_argument, 0, 'r'}, {"skip", required_argument, 0, 's'}, {"length", required_argument, 0, 'n'}, + {"diskcount", required_argument, 0, 'c'}, {"debug", required_argument, 0, 'd'}, - {"raw", no_argument, 0, 'r'}, - {"long", no_argument, 0, 'l'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"verbose", no_argument, 0, 'v'}, - {0, 0, 0, 0} }; @@ -351,17 +409,17 @@ Usage: grub-fstest [OPTION]... IMAGE_PATH COMMANDS\n\ Debug tool for filesystem driver.\n\ \nCommands:\n\ ls PATH list files in PATH\n\ - cp SRC DEST copy file to local system\n\ - cmp SRC DEST compare files\n\ - hex FILE hex dump FILE\n\ + cp FILE LOCAL copy FILE to local file LOCAL\n\ + cmp FILE LOCAL compare FILE with local file LOCAL\n\ + hex FILE Hex dump FILE\n\ + crc FILE Get crc32 checksum of FILE\n\ blocklist FILE display blocklist of FILE\n\ \nOptions:\n\ - -p, --part=NUM select partition NUM\n\ + -r, --root=DEVICE_NAME set root device\n\ -s, --skip=N skip N bytes from output file\n\ -n, --length=N handle N bytes in output file\n\ + -c, --diskcount=N N input files\n\ -d, --debug=S Set debug environment variable\n\ - -r, --raw disable auto decompression\n\ - -l, --long show long directory list\n\ -h, --help display this message and exit\n\ -V, --version print version information and exit\n\ -v, --verbose print verbose messages\n\ @@ -374,45 +432,66 @@ Report bugs to <%s>.\n", PACKAGE_BUGREPORT); int main (int argc, char *argv[]) { - char *image_path, *debug_str = 0; - int cmd, is_raw = 0, is_long = 0; + char *debug_str = 0, *root = 0, *default_root, *alloc_root; + int i, cmd, num_opts, image_index, num_disks = 1; progname = "grub-fstest"; + /* Find the first non option entry. */ + for (num_opts = 1; num_opts < argc; num_opts++) + if (argv[num_opts][0] == '-') + { + if ((argv[num_opts][2] == 0) && (num_opts < argc - 1) && + ((argv[num_opts][1] == 'r') || + (argv[num_opts][1] == 's') || + (argv[num_opts][1] == 'n') || + (argv[num_opts][1] == 'c') || + (argv[num_opts][1] == 'd'))) + num_opts++; + } + else + break; + /* Check for options. */ while (1) { - int c = getopt_long (argc, argv, "p:s:n:d:rlhVv", options, 0); + int c = getopt_long (num_opts, argv, "r:s:n:c:d:hVv", options, 0); + char *p; if (c == -1) break; else switch (c) { - case 'p': - part = optarg; + case 'r': + root = optarg; break; case 's': - skip = grub_strtoul (optarg, NULL, 0); + skip = grub_strtoul (optarg, &p, 0); + if (*p == 's') + skip <<= GRUB_DISK_SECTOR_BITS; break; case 'n': - leng = grub_strtoul (optarg, NULL, 0); + leng = grub_strtoul (optarg, &p, 0); + if (*p == 's') + leng <<= GRUB_DISK_SECTOR_BITS; break; + case 'c': + num_disks = grub_strtoul (optarg, NULL, 0); + if (num_disks < 1) + { + fprintf (stderr, "Invalid disk count.\n"); + usage (1); + } + break; + case 'd': debug_str = optarg; break; - case 'r': - is_raw = 1; - break; - - case 'l': - is_long = 1; - break; - case 'h': usage (0); break; @@ -432,52 +511,53 @@ main (int argc, char *argv[]) } /* Obtain PATH. */ - if (optind >= argc) + if (optind + num_disks - 1 >= argc) { - fprintf (stderr, "No path is specified.\n"); + fprintf (stderr, "Not enough pathname.\n"); usage (1); } - image_path = argv[optind]; - - if (*image_path != '/') - { - fprintf (stderr, "Must use absolute path.\n"); - usage (1); - } - - optind++; + image_index = optind; + for (i = 0; i < num_disks; i++, optind++) + if (argv[optind][0] != '/') + { + fprintf (stderr, "Must use absolute path.\n"); + usage (1); + } cmd = 0; if (optind < argc) { - int nparm = 1; + int nparm = 0; if (!grub_strcmp (argv[optind], "ls")) - { - cmd = CMD_LS; - if (is_long) - argv[optind--] = "-l"; - else - nparm = 0; - } + { + cmd = CMD_LS; + } else if (!grub_strcmp (argv[optind], "cp")) { cmd = CMD_CP; - nparm = 2; + nparm = 2; } else if (!grub_strcmp (argv[optind], "cmp")) { cmd = CMD_CMP; - nparm = 2; + nparm = 2; } else if (!grub_strcmp (argv[optind], "hex")) { cmd = CMD_HEX; + nparm = 1; + } + else if (!grub_strcmp (argv[optind], "crc")) + { + cmd = CMD_CRC; + nparm = 1; } else if (!grub_strcmp (argv[optind], "blocklist")) { cmd = CMD_BLOCKLIST; + nparm = 1; } else { @@ -503,14 +583,31 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); - if (is_raw) - grub_env_set ("filehook", "0"); - if (debug_str) grub_env_set ("debug", debug_str); + default_root = (num_disks == 1) ? "loop0" : "md0"; + alloc_root = 0; + if (root) + { + if ((*root >= '0') && (*root <= '9')) + { + alloc_root = xmalloc (strlen (default_root) + strlen (root) + 2); + + sprintf (alloc_root, "%s,%s", default_root, root); + root = alloc_root; + } + } + else + root = default_root; + + grub_env_set ("root", root); + + if (alloc_root) + free (alloc_root); + /* Do it. */ - fstest (image_path + 1, cmd, argc - optind, argv + optind); + fstest (argv + image_index, num_disks, cmd, argc - optind, argv + optind); /* Free resources. */ grub_fini_all (); diff --git a/util/grub-probe.c b/util/grub-probe.c index a4f51e2..ce9cbff 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -142,7 +142,7 @@ probe (const char *path, char *device_name) abstraction_name = "lvm"; break; case GRUB_DEV_ABSTRACTION_RAID: - abstraction_name = "raid"; + abstraction_name = "raid mdraid"; break; default: grub_util_info ("did not find LVM/RAID in %s, assuming raw device", device_name); ^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH] Split of raid scan code 2008-08-18 17:20 ` Bean @ 2008-08-23 14:51 ` Bean 0 siblings, 0 replies; 24+ messages in thread From: Bean @ 2008-08-23 14:51 UTC (permalink / raw) To: The development of GRUB 2 On Tue, Aug 19, 2008 at 1:20 AM, Bean <bean123ch@gmail.com> wrote: > Hi, > > This patch fix a bug in total_devs and grub_raid_memberlist, it also > resolve the conflict with current svn head. Committed. -- Bean ^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2008-08-23 14:51 UTC | newest] Thread overview: 24+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-08-10 11:52 [PATCH] Split of raid scan code Bean 2008-08-10 12:13 ` Felix Zielcke 2008-08-10 14:29 ` Robert Millan 2008-08-10 14:45 ` Bean 2008-08-10 15:49 ` Bean 2008-08-11 18:45 ` Bean 2008-08-11 20:13 ` Bean 2008-08-11 20:24 ` Bean 2008-08-11 20:30 ` Robert Millan 2008-08-12 11:49 ` Bean 2008-08-12 12:24 ` Felix Zielcke 2008-08-12 13:32 ` Robert Millan 2008-08-13 10:05 ` Marco Gerards 2008-08-13 18:43 ` Bean 2008-08-13 20:01 ` Bean 2008-08-16 9:07 ` Bean 2008-08-16 12:16 ` Robert Millan 2008-08-16 15:20 ` Bean 2008-08-17 14:19 ` Bean 2008-08-17 14:52 ` Robert Millan 2008-08-17 15:08 ` Bean 2008-08-17 15:23 ` Robert Millan 2008-08-18 17:20 ` Bean 2008-08-23 14:51 ` Bean
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.