| /* dmraid_nvidia.c - module to handle Nvidia fakeraid. */ |
| /* |
| * GRUB -- GRand Unified Bootloader |
| * Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc. |
| * |
| * GRUB is free software: you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation, either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * GRUB is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with GRUB. If not, see <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/diskfilter.h> |
| |
| GRUB_MOD_LICENSE ("GPLv3+"); |
| |
| #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 offline. */ |
| #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 */ |
| } GRUB_PACKED; |
| |
| static struct grub_diskfilter_vg * |
| grub_dmraid_nv_detect (grub_disk_t disk, |
| struct grub_diskfilter_pv_id *id, |
| grub_disk_addr_t *start_sector) |
| { |
| grub_disk_addr_t sector; |
| struct grub_nv_super sb; |
| int level; |
| grub_uint64_t disk_size; |
| grub_uint32_t capacity; |
| grub_uint8_t total_volumes; |
| char *uuid; |
| |
| if (disk->partition) |
| /* Skip partition. */ |
| return NULL; |
| |
| sector = grub_disk_get_size (disk); |
| if (sector == GRUB_DISK_SIZE_UNKNOWN) |
| /* Not raid. */ |
| return NULL; |
| sector -= 2; |
| if (grub_disk_read (disk, sector, 0, sizeof (sb), &sb)) |
| return NULL; |
| |
| if (grub_memcmp (sb.vendor, NV_ID_STRING, 6)) |
| /* Not raid. */ |
| return NULL; |
| |
| if (sb.version != NV_VERSION) |
| { |
| grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, |
| "unknown version: %d.%d", sb.version); |
| return NULL; |
| } |
| |
| capacity = grub_le_to_cpu32 (sb.capacity); |
| total_volumes = sb.array.total_volumes; |
| |
| switch (sb.array.raid_level) |
| { |
| case NV_LEVEL_0: |
| level = 0; |
| if (total_volumes == 0) |
| /* Not RAID. */ |
| return NULL; |
| disk_size = capacity / total_volumes; |
| break; |
| |
| case NV_LEVEL_1: |
| level = 1; |
| disk_size = sb.capacity; |
| break; |
| |
| case NV_LEVEL_5: |
| level = 5; |
| if (total_volumes == 0 || total_volumes == 1) |
| /* Not RAID. */ |
| return NULL; |
| disk_size = capacity / (total_volumes - 1); |
| break; |
| |
| default: |
| grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, |
| "unsupported RAID level: %d", sb.array.raid_level); |
| return NULL; |
| } |
| |
| uuid = grub_malloc (sizeof (sb.array.signature)); |
| if (! uuid) |
| return NULL; |
| |
| grub_memcpy (uuid, (char *) &sb.array.signature, |
| sizeof (sb.array.signature)); |
| |
| id->uuidlen = 0; |
| id->id = sb.unit_number; |
| |
| *start_sector = 0; |
| |
| return grub_diskfilter_make_raid (sizeof (sb.array.signature), |
| uuid, sb.array.total_volumes, |
| "nv", disk_size, |
| sb.array.stripe_block_size, |
| GRUB_RAID_LAYOUT_LEFT_ASYMMETRIC, |
| level); |
| } |
| |
| static struct grub_diskfilter grub_dmraid_nv_dev = |
| { |
| .name = "dmraid_nv", |
| .detect = grub_dmraid_nv_detect, |
| .next = 0 |
| }; |
| |
| GRUB_MOD_INIT(dm_nv) |
| { |
| grub_diskfilter_register_front (&grub_dmraid_nv_dev); |
| } |
| |
| GRUB_MOD_FINI(dm_nv) |
| { |
| grub_diskfilter_unregister (&grub_dmraid_nv_dev); |
| } |