]>
Commit | Line | Data |
---|---|---|
c8ec30a0 | 1 | /* mdraid_linux.c - module to handle Linux Software RAID. */ |
5ed20adc | 2 | /* |
3 | * GRUB -- GRand Unified Bootloader | |
31cfe714 | 4 | * Copyright (C) 2008,2009,2010 Free Software Foundation, Inc. |
5ed20adc | 5 | * |
6 | * GRUB is free software: you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation, either version 3 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * GRUB is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include <grub/dl.h> | |
21 | #include <grub/disk.h> | |
22 | #include <grub/mm.h> | |
23 | #include <grub/err.h> | |
24 | #include <grub/misc.h> | |
076e7c0f | 25 | #include <grub/diskfilter.h> |
5ed20adc | 26 | |
27 | /* Linux RAID on disk structures and constants, | |
28 | copied from include/linux/raid/md_p.h. */ | |
29 | ||
e745cf0c VS |
30 | GRUB_MOD_LICENSE ("GPLv3+"); |
31 | ||
22e6a774 VS |
32 | #ifdef MODE_BIGENDIAN |
33 | #define grub_md_to_cpu64 grub_be_to_cpu64 | |
34 | #define grub_md_to_cpu32 grub_be_to_cpu32 | |
35 | #define grub_md_to_cpu16 grub_be_to_cpu16 | |
954fe771 AB |
36 | #define grub_cpu_to_md64_compile_time grub_cpu_to_be64_compile_time |
37 | #define grub_cpu_to_md32_compile_time grub_cpu_to_be32_compile_time | |
38 | #define grub_cpu_to_md16_compile_time grub_cpu_to_be16_compile_time | |
22e6a774 VS |
39 | #else |
40 | #define grub_md_to_cpu64 grub_le_to_cpu64 | |
41 | #define grub_md_to_cpu32 grub_le_to_cpu32 | |
42 | #define grub_md_to_cpu16 grub_le_to_cpu16 | |
954fe771 AB |
43 | #define grub_cpu_to_md64_compile_time grub_cpu_to_le64_compile_time |
44 | #define grub_cpu_to_md32_compile_time grub_cpu_to_le32_compile_time | |
45 | #define grub_cpu_to_md16_compile_time grub_cpu_to_le16_compile_time | |
22e6a774 VS |
46 | #endif |
47 | ||
5ed20adc | 48 | #define RESERVED_BYTES (64 * 1024) |
49 | #define RESERVED_SECTORS (RESERVED_BYTES / 512) | |
50 | ||
51 | #define NEW_SIZE_SECTORS(x) ((x & ~(RESERVED_SECTORS - 1)) \ | |
52 | - RESERVED_SECTORS) | |
53 | ||
54 | #define SB_BYTES 4096 | |
55 | #define SB_WORDS (SB_BYTES / 4) | |
56 | #define SB_SECTORS (SB_BYTES / 512) | |
57 | ||
58 | /* | |
59 | * The following are counted in 32-bit words | |
60 | */ | |
61 | #define SB_GENERIC_OFFSET 0 | |
62 | ||
63 | #define SB_PERSONALITY_OFFSET 64 | |
64 | #define SB_DISKS_OFFSET 128 | |
65 | #define SB_DESCRIPTOR_OFFSET 992 | |
66 | ||
67 | #define SB_GENERIC_CONSTANT_WORDS 32 | |
68 | #define SB_GENERIC_STATE_WORDS 32 | |
69 | #define SB_GENERIC_WORDS (SB_GENERIC_CONSTANT_WORDS + \ | |
70 | SB_GENERIC_STATE_WORDS) | |
71 | ||
72 | #define SB_PERSONALITY_WORDS 64 | |
73 | #define SB_DESCRIPTOR_WORDS 32 | |
74 | #define SB_DISKS 27 | |
75 | #define SB_DISKS_WORDS (SB_DISKS * SB_DESCRIPTOR_WORDS) | |
76 | ||
77 | #define SB_RESERVED_WORDS (1024 \ | |
78 | - SB_GENERIC_WORDS \ | |
79 | - SB_PERSONALITY_WORDS \ | |
80 | - SB_DISKS_WORDS \ | |
81 | - SB_DESCRIPTOR_WORDS) | |
82 | ||
83 | #define SB_EQUAL_WORDS (SB_GENERIC_WORDS \ | |
84 | + SB_PERSONALITY_WORDS \ | |
85 | + SB_DISKS_WORDS) | |
86 | ||
87 | /* | |
88 | * Device "operational" state bits | |
89 | */ | |
90 | #define DISK_FAULTY 0 | |
91 | #define DISK_ACTIVE 1 | |
92 | #define DISK_SYNC 2 | |
93 | #define DISK_REMOVED 3 | |
94 | ||
95 | #define DISK_WRITEMOSTLY 9 | |
96 | ||
97 | #define SB_MAGIC 0xa92b4efc | |
98 | ||
99 | /* | |
100 | * Superblock state bits | |
101 | */ | |
102 | #define SB_CLEAN 0 | |
103 | #define SB_ERRORS 1 | |
104 | ||
105 | #define SB_BITMAP_PRESENT 8 | |
106 | ||
107 | struct grub_raid_disk_09 | |
108 | { | |
109 | grub_uint32_t number; /* Device number in the entire set. */ | |
110 | grub_uint32_t major; /* Device major number. */ | |
111 | grub_uint32_t minor; /* Device minor number. */ | |
112 | grub_uint32_t raid_disk; /* The role of the device in the raid set. */ | |
113 | grub_uint32_t state; /* Operational state. */ | |
114 | grub_uint32_t reserved[SB_DESCRIPTOR_WORDS - 5]; | |
115 | }; | |
116 | ||
117 | struct grub_raid_super_09 | |
118 | { | |
119 | /* | |
120 | * Constant generic information | |
121 | */ | |
122 | grub_uint32_t md_magic; /* MD identifier. */ | |
123 | grub_uint32_t major_version; /* Major version. */ | |
124 | grub_uint32_t minor_version; /* Minor version. */ | |
125 | grub_uint32_t patch_version; /* Patchlevel version. */ | |
126 | grub_uint32_t gvalid_words; /* Number of used words in this section. */ | |
127 | grub_uint32_t set_uuid0; /* Raid set identifier. */ | |
128 | grub_uint32_t ctime; /* Creation time. */ | |
129 | grub_uint32_t level; /* Raid personality. */ | |
130 | grub_uint32_t size; /* Apparent size of each individual disk. */ | |
131 | grub_uint32_t nr_disks; /* Total disks in the raid set. */ | |
132 | grub_uint32_t raid_disks; /* Disks in a fully functional raid set. */ | |
133 | grub_uint32_t md_minor; /* Preferred MD minor device number. */ | |
134 | grub_uint32_t not_persistent; /* Does it have a persistent superblock. */ | |
135 | grub_uint32_t set_uuid1; /* Raid set identifier #2. */ | |
136 | grub_uint32_t set_uuid2; /* Raid set identifier #3. */ | |
137 | grub_uint32_t set_uuid3; /* Raid set identifier #4. */ | |
138 | grub_uint32_t gstate_creserved[SB_GENERIC_CONSTANT_WORDS - 16]; | |
139 | ||
140 | /* | |
141 | * Generic state information | |
142 | */ | |
143 | grub_uint32_t utime; /* Superblock update time. */ | |
144 | grub_uint32_t state; /* State bits (clean, ...). */ | |
145 | grub_uint32_t active_disks; /* Number of currently active disks. */ | |
146 | grub_uint32_t working_disks; /* Number of working disks. */ | |
147 | grub_uint32_t failed_disks; /* Number of failed disks. */ | |
148 | grub_uint32_t spare_disks; /* Number of spare disks. */ | |
149 | grub_uint32_t sb_csum; /* Checksum of the whole superblock. */ | |
150 | grub_uint64_t events; /* Superblock update count. */ | |
151 | grub_uint64_t cp_events; /* Checkpoint update count. */ | |
152 | grub_uint32_t recovery_cp; /* Recovery checkpoint sector count. */ | |
153 | grub_uint32_t gstate_sreserved[SB_GENERIC_STATE_WORDS - 12]; | |
154 | ||
155 | /* | |
156 | * Personality information | |
157 | */ | |
158 | grub_uint32_t layout; /* The array's physical layout. */ | |
159 | grub_uint32_t chunk_size; /* Chunk size in bytes. */ | |
160 | grub_uint32_t root_pv; /* LV root PV. */ | |
161 | grub_uint32_t root_block; /* LV root block. */ | |
162 | grub_uint32_t pstate_reserved[SB_PERSONALITY_WORDS - 4]; | |
163 | ||
164 | /* | |
165 | * Disks information | |
166 | */ | |
167 | struct grub_raid_disk_09 disks[SB_DISKS]; | |
168 | ||
169 | /* | |
170 | * Reserved | |
171 | */ | |
172 | grub_uint32_t reserved[SB_RESERVED_WORDS]; | |
173 | ||
174 | /* | |
175 | * Active descriptor | |
176 | */ | |
177 | struct grub_raid_disk_09 this_disk; | |
7e47e27b | 178 | } GRUB_PACKED; |
5ed20adc | 179 | |
076e7c0f VS |
180 | static struct grub_diskfilter_vg * |
181 | grub_mdraid_detect (grub_disk_t disk, | |
182 | struct grub_diskfilter_pv_id *id, | |
1e8d555b | 183 | grub_disk_addr_t *start_sector) |
5ed20adc | 184 | { |
1e8d555b VS |
185 | grub_disk_addr_t sector; |
186 | grub_uint64_t size; | |
e6a6182d | 187 | struct grub_raid_super_09 *sb = NULL; |
5ed20adc | 188 | grub_uint32_t *uuid; |
c0cf26da | 189 | grub_uint32_t level; |
e6a6182d | 190 | struct grub_diskfilter_vg *ret; |
c8ec30a0 | 191 | |
1e8d555b VS |
192 | /* The sector where the mdraid 0.90 superblock is stored, if available. */ |
193 | size = grub_disk_get_size (disk); | |
7b61e609 | 194 | if (size == GRUB_DISK_SIZE_UNKNOWN) |
b2582b84 VS |
195 | /* not 0.9x raid. */ |
196 | return NULL; | |
1e8d555b VS |
197 | sector = NEW_SIZE_SECTORS (size); |
198 | ||
e6a6182d VS |
199 | sb = grub_malloc (sizeof (*sb)); |
200 | if (!sb) | |
076e7c0f | 201 | return NULL; |
1e8d555b | 202 | |
e6a6182d VS |
203 | if (grub_disk_read (disk, sector, 0, SB_BYTES, sb)) |
204 | goto fail; | |
205 | ||
1e8d555b | 206 | /* Look whether there is a mdraid 0.90 superblock. */ |
e6a6182d | 207 | if (sb->md_magic != grub_cpu_to_md32_compile_time (SB_MAGIC)) |
b2582b84 | 208 | /* not 0.9x raid. */ |
e6a6182d | 209 | goto fail; |
1e8d555b | 210 | |
e6a6182d VS |
211 | if (sb->major_version != grub_cpu_to_md32_compile_time (0) |
212 | || sb->minor_version != grub_cpu_to_md32_compile_time (90)) | |
b2582b84 | 213 | /* Unsupported version. */ |
e6a6182d | 214 | goto fail; |
5ed20adc | 215 | |
e6a6182d | 216 | /* No need for explicit check that sb->size is 0 (unspecified) since |
1e9a9a3f | 217 | 0 >= non-0 is false. */ |
e6a6182d VS |
218 | if (((grub_disk_addr_t) grub_md_to_cpu32 (sb->size)) * 2 >= size) |
219 | goto fail; | |
1e9a9a3f | 220 | |
c8ec30a0 | 221 | /* FIXME: Check the checksum. */ |
5ed20adc | 222 | |
e6a6182d | 223 | level = grub_md_to_cpu32 (sb->level); |
5ed20adc | 224 | /* Multipath. */ |
c0cf26da VS |
225 | if ((int) level == -4) |
226 | level = 1; | |
5ed20adc | 227 | |
c0cf26da VS |
228 | if (level != 0 && level != 1 && level != 4 && |
229 | level != 5 && level != 6 && level != 10) | |
076e7c0f VS |
230 | { |
231 | grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | |
232 | "unsupported RAID level: %d", level); | |
e6a6182d | 233 | goto fail; |
076e7c0f | 234 | } |
e6a6182d VS |
235 | if (grub_md_to_cpu32 (sb->this_disk.number) == 0xffff |
236 | || grub_md_to_cpu32 (sb->this_disk.number) == 0xfffe) | |
b2582b84 | 237 | /* Spares aren't implemented. */ |
e6a6182d | 238 | goto fail; |
076e7c0f | 239 | |
564840dc | 240 | uuid = grub_malloc (16); |
076e7c0f | 241 | if (!uuid) |
e6a6182d | 242 | goto fail; |
5ed20adc | 243 | |
e6a6182d VS |
244 | uuid[0] = grub_swap_bytes32 (sb->set_uuid0); |
245 | uuid[1] = grub_swap_bytes32 (sb->set_uuid1); | |
246 | uuid[2] = grub_swap_bytes32 (sb->set_uuid2); | |
247 | uuid[3] = grub_swap_bytes32 (sb->set_uuid3); | |
5ed20adc | 248 | |
1c785436 CW |
249 | *start_sector = 0; |
250 | ||
076e7c0f | 251 | id->uuidlen = 0; |
e6a6182d | 252 | id->id = grub_md_to_cpu32 (sb->this_disk.number); |
076e7c0f VS |
253 | |
254 | char buf[32]; | |
e6a6182d VS |
255 | grub_snprintf (buf, sizeof (buf), "md%d", grub_md_to_cpu32 (sb->md_minor)); |
256 | ret = grub_diskfilter_make_raid (16, (char *) uuid, | |
257 | grub_md_to_cpu32 (sb->raid_disks), buf, | |
258 | (sb->size) ? ((grub_disk_addr_t) | |
259 | grub_md_to_cpu32 (sb->size)) * 2 | |
260 | : sector, | |
261 | grub_md_to_cpu32 (sb->chunk_size) >> 9, | |
262 | grub_md_to_cpu32 (sb->layout), | |
263 | level); | |
264 | grub_free (sb); | |
265 | return ret; | |
266 | ||
267 | fail: | |
268 | grub_free (sb); | |
269 | return NULL; | |
68ac8c8d | 270 | } |
c8ec30a0 | 271 | |
076e7c0f | 272 | static struct grub_diskfilter grub_mdraid_dev = { |
22e6a774 VS |
273 | #ifdef MODE_BIGENDIAN |
274 | .name = "mdraid09_be", | |
275 | #else | |
1e8d555b | 276 | .name = "mdraid09", |
22e6a774 | 277 | #endif |
5ed20adc | 278 | .detect = grub_mdraid_detect, |
279 | .next = 0 | |
280 | }; | |
281 | ||
22e6a774 VS |
282 | #ifdef MODE_BIGENDIAN |
283 | GRUB_MOD_INIT (mdraid09_be) | |
284 | #else | |
1e8d555b | 285 | GRUB_MOD_INIT (mdraid09) |
22e6a774 | 286 | #endif |
5ed20adc | 287 | { |
b72d44a1 | 288 | grub_diskfilter_register_front (&grub_mdraid_dev); |
5ed20adc | 289 | } |
290 | ||
22e6a774 VS |
291 | #ifdef MODE_BIGENDIAN |
292 | GRUB_MOD_FINI (mdraid09_be) | |
293 | #else | |
1e8d555b | 294 | GRUB_MOD_FINI (mdraid09) |
22e6a774 | 295 | #endif |
5ed20adc | 296 | { |
076e7c0f | 297 | grub_diskfilter_unregister (&grub_mdraid_dev); |
5ed20adc | 298 | } |