1 | /* $NetBSD: ata_raid_intel.c,v 1.7 2014/03/25 16:19:13 christos Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2000-2008 Søren Schmidt <sos@FreeBSD.org> |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer, |
12 | * without modification, immediately at the beginning of the file. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * |
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | /* |
30 | * Support for parsing Intel MatrixRAID controller configuration blocks. |
31 | * |
32 | * Adapted to NetBSD by Juan Romero Pardines (xtraeme@gmail.org). |
33 | */ |
34 | |
35 | #include <sys/cdefs.h> |
36 | __KERNEL_RCSID(0, "$NetBSD: ata_raid_intel.c,v 1.7 2014/03/25 16:19:13 christos Exp $" ); |
37 | |
38 | #include <sys/param.h> |
39 | #include <sys/buf.h> |
40 | #include <sys/bufq.h> |
41 | #include <sys/conf.h> |
42 | #include <sys/device.h> |
43 | #include <sys/disk.h> |
44 | #include <sys/disklabel.h> |
45 | #include <sys/fcntl.h> |
46 | #include <sys/malloc.h> |
47 | #include <sys/vnode.h> |
48 | #include <sys/kauth.h> |
49 | |
50 | #include <miscfs/specfs/specdev.h> |
51 | |
52 | #include <dev/ata/atareg.h> |
53 | #include <dev/ata/atavar.h> |
54 | #include <dev/ata/wdvar.h> |
55 | |
56 | #include <dev/ata/ata_raidreg.h> |
57 | #include <dev/ata/ata_raidvar.h> |
58 | |
59 | #ifdef ATA_RAID_DEBUG |
60 | #define DPRINTF(x) printf x |
61 | #else |
62 | #define DPRINTF(x) /* nothing */ |
63 | #endif |
64 | |
65 | static int find_volume_id(struct intel_raid_conf *); |
66 | |
67 | |
68 | #ifdef ATA_RAID_DEBUG |
69 | static const char * |
70 | ata_raid_intel_type(int type) |
71 | { |
72 | static char buffer[16]; |
73 | |
74 | switch (type) { |
75 | case INTEL_T_RAID0: |
76 | return "RAID0" ; |
77 | case INTEL_T_RAID1: |
78 | return "RAID1" ; |
79 | case INTEL_T_RAID5: |
80 | return "RAID5" ; |
81 | default: |
82 | snprintf(buffer, sizeof(buffer), "UNKNOWN 0x%02x" , type); |
83 | return buffer; |
84 | } |
85 | } |
86 | |
87 | static void |
88 | ata_raid_intel_print_info(struct intel_raid_conf *info) |
89 | { |
90 | struct intel_raid_mapping *map; |
91 | int i, j; |
92 | |
93 | printf("********* ATA Intel MatrixRAID Metadata *********\n" ); |
94 | printf("intel_id <%.24s>\n" , info->intel_id); |
95 | printf("version <%.6s>\n" , info->version); |
96 | printf("checksum 0x%08x\n" , info->checksum); |
97 | printf("config_size 0x%08x\n" , info->config_size); |
98 | printf("config_id 0x%08x\n" , info->config_id); |
99 | printf("generation 0x%08x\n" , info->generation); |
100 | printf("total_disks %u\n" , info->total_disks); |
101 | printf("total_volumes %u\n" , info->total_volumes); |
102 | printf("DISK# serial disk sectors disk_id flags\n" ); |
103 | for (i = 0; i < info->total_disks; i++) { |
104 | printf(" %d <%.16s> %u 0x%08x 0x%08x\n" , |
105 | i, info->disk[i].serial, info->disk[i].sectors, |
106 | info->disk[i].id, info->disk[i].flags); |
107 | } |
108 | |
109 | map = (struct intel_raid_mapping *)&info->disk[info->total_disks]; |
110 | for (j = 0; j < info->total_volumes; j++) { |
111 | printf("name %.16s\n" , map->name); |
112 | printf("total_sectors %ju\n" , map->total_sectors); |
113 | printf("state %u\n" , map->state); |
114 | printf("reserved %u\n" , map->reserved); |
115 | printf("offset %u\n" , map->offset); |
116 | printf("disk_sectors %u\n" , map->disk_sectors); |
117 | printf("stripe_count %u\n" , map->stripe_count); |
118 | printf("stripe_sectors %u\n" , map->stripe_sectors); |
119 | printf("status %u\n" , map->status); |
120 | printf("type %s\n" , ata_raid_intel_type(map->type)); |
121 | printf("total_disks %u\n" , map->total_disks); |
122 | printf("magic[0] 0x%02x\n" , map->magic[0]); |
123 | printf("magic[1] 0x%02x\n" , map->magic[1]); |
124 | printf("magic[2] 0x%02x\n" , map->magic[2]); |
125 | for (i = 0; i < map->total_disks; i++) |
126 | printf(" disk %d at disk_idx 0x%08x\n" , |
127 | i, map->disk_idx[i]); |
128 | |
129 | map = (struct intel_raid_mapping *) |
130 | &map->disk_idx[map->total_disks]; |
131 | } |
132 | printf("=================================================\n" ); |
133 | } |
134 | #endif |
135 | |
136 | int |
137 | ata_raid_read_config_intel(struct wd_softc *sc) |
138 | { |
139 | struct intel_raid_conf *info; |
140 | struct intel_raid_mapping *map; |
141 | struct ataraid_array_info *aai; |
142 | struct ataraid_disk_info *adi; |
143 | struct vnode *vp; |
144 | uint32_t checksum, *ptr; |
145 | int bmajor, count, curvol = 0, error = 0; |
146 | char *tmp; |
147 | dev_t dev; |
148 | int volumeid, diskidx; |
149 | |
150 | info = malloc(1536, M_DEVBUF, M_WAITOK|M_ZERO); |
151 | |
152 | bmajor = devsw_name2blk(device_xname(sc->sc_dev), NULL, 0); |
153 | |
154 | /* Get a vnode for the raw partition of this disk. */ |
155 | dev = MAKEDISKDEV(bmajor, device_unit(sc->sc_dev), RAW_PART); |
156 | error = bdevvp(dev, &vp); |
157 | if (error) |
158 | goto out; |
159 | |
160 | error = VOP_OPEN(vp, FREAD, NOCRED); |
161 | if (error) { |
162 | vput(vp); |
163 | goto out; |
164 | } |
165 | |
166 | error = ata_raid_config_block_rw(vp, INTEL_LBA(sc), info, |
167 | 1024, B_READ); |
168 | VOP_CLOSE(vp, FREAD, NOCRED); |
169 | vput(vp); |
170 | if (error) { |
171 | DPRINTF(("%s: error %d reading Intel MatrixRAID config block\n" , |
172 | device_xname(sc->sc_dev), error)); |
173 | goto out; |
174 | } |
175 | |
176 | tmp = (char *)info; |
177 | (void)memcpy(tmp + 1024, tmp, 512); |
178 | (void)memcpy(tmp, tmp + 512, 1024); |
179 | (void)memset(tmp + 1024, 0, 512); |
180 | |
181 | /* Check if this is a Intel RAID struct */ |
182 | if (strncmp(info->intel_id, INTEL_MAGIC, strlen(INTEL_MAGIC))) { |
183 | DPRINTF(("%s: Intel MatrixRAID signature check failed\n" , |
184 | device_xname(sc->sc_dev))); |
185 | error = ESRCH; |
186 | goto out; |
187 | } |
188 | |
189 | /* calculate checksum and compare for valid */ |
190 | for (checksum = 0, ptr = (uint32_t *)info, count = 0; |
191 | count < (info->config_size / sizeof(uint32_t)); count++) |
192 | checksum += *ptr++; |
193 | |
194 | checksum -= info->checksum; |
195 | if (checksum != info->checksum) { |
196 | DPRINTF(("%s: Intel MatrixRAID checksum failed 0x%x != 0x%x\n" , |
197 | device_xname(sc->sc_dev), checksum, info->checksum)); |
198 | error = ESRCH; |
199 | goto out; |
200 | } |
201 | |
202 | #ifdef ATA_RAID_DEBUG |
203 | ata_raid_intel_print_info(info); |
204 | #endif |
205 | |
206 | /* This one points to the first volume */ |
207 | map = (struct intel_raid_mapping *)&info->disk[info->total_disks]; |
208 | |
209 | volumeid = find_volume_id(info); |
210 | if (volumeid < 0) { |
211 | aprint_error_dev(sc->sc_dev, |
212 | "too many RAID arrays\n" ); |
213 | error = ENOMEM; |
214 | goto out; |
215 | } |
216 | |
217 | findvol: |
218 | /* |
219 | * Lookup or allocate a new array info structure for this array. |
220 | */ |
221 | aai = ata_raid_get_array_info(ATA_RAID_TYPE_INTEL, volumeid + curvol); |
222 | |
223 | /* Fill in array info */ |
224 | aai->aai_generation = info->generation; |
225 | aai->aai_status = AAI_S_READY; |
226 | |
227 | switch (map->type) { |
228 | case INTEL_T_RAID0: |
229 | aai->aai_level = AAI_L_RAID0; |
230 | aai->aai_width = map->total_disks; |
231 | break; |
232 | case INTEL_T_RAID1: |
233 | aai->aai_level = AAI_L_RAID1; |
234 | aai->aai_width = 1; |
235 | break; |
236 | default: |
237 | DPRINTF(("%s: unknown Intel MatrixRAID type 0x%02x\n" , |
238 | device_xname(sc->sc_dev), map->type)); |
239 | error = EINVAL; |
240 | goto out; |
241 | } |
242 | |
243 | switch (map->state) { |
244 | case INTEL_S_DEGRADED: |
245 | aai->aai_status |= AAI_S_DEGRADED; |
246 | break; |
247 | case INTEL_S_DISABLED: |
248 | case INTEL_S_FAILURE: |
249 | aai->aai_status &= ~AAI_S_READY; |
250 | break; |
251 | } |
252 | |
253 | aai->aai_type = ATA_RAID_TYPE_INTEL; |
254 | aai->aai_capacity = map->total_sectors; |
255 | aai->aai_interleave = map->stripe_sectors; |
256 | aai->aai_ndisks = map->total_disks; |
257 | aai->aai_heads = 255; |
258 | aai->aai_sectors = 63; |
259 | aai->aai_cylinders = |
260 | aai->aai_capacity / (aai->aai_heads * aai->aai_sectors); |
261 | aai->aai_offset = map->offset; |
262 | aai->aai_reserved = 3; |
263 | if (map->name) |
264 | strlcpy(aai->aai_name, map->name, sizeof(aai->aai_name)); |
265 | |
266 | /* Fill in disk info */ |
267 | diskidx = aai->aai_curdisk++; |
268 | adi = &aai->aai_disks[diskidx]; |
269 | adi->adi_status = 0; |
270 | |
271 | if (info->disk[diskidx].flags & INTEL_F_ONLINE) |
272 | adi->adi_status |= ADI_S_ONLINE; |
273 | if (info->disk[diskidx].flags & INTEL_F_ASSIGNED) |
274 | adi->adi_status |= ADI_S_ASSIGNED; |
275 | if (info->disk[diskidx].flags & INTEL_F_SPARE) { |
276 | adi->adi_status &= ~ADI_S_ONLINE; |
277 | adi->adi_status |= ADI_S_SPARE; |
278 | } |
279 | if (info->disk[diskidx].flags & INTEL_F_DOWN) |
280 | adi->adi_status &= ~ADI_S_ONLINE; |
281 | |
282 | if (adi->adi_status) { |
283 | adi->adi_dev = sc->sc_dev; |
284 | adi->adi_sectors = info->disk[diskidx].sectors; |
285 | adi->adi_compsize = adi->adi_sectors - aai->aai_reserved; |
286 | |
287 | /* |
288 | * Check if that is the only volume, otherwise repeat |
289 | * the process to find more. |
290 | */ |
291 | if ((curvol + 1) < info->total_volumes) { |
292 | curvol++; |
293 | map = (struct intel_raid_mapping *) |
294 | &map->disk_idx[map->total_disks]; |
295 | goto findvol; |
296 | } |
297 | } |
298 | |
299 | out: |
300 | free(info, M_DEVBUF); |
301 | return error; |
302 | } |
303 | |
304 | |
305 | /* |
306 | * Assign `volume id' to RAID volumes. |
307 | */ |
308 | static struct { |
309 | /* We assume disks are on the same array if these three values |
310 | are same. */ |
311 | uint32_t config_id; |
312 | uint32_t generation; |
313 | uint32_t checksum; |
314 | |
315 | int id; |
316 | } array_note[10]; /* XXX: this array is not used after ld_ataraid is |
317 | * configured. */ |
318 | |
319 | static int n_array = 0; |
320 | static int volume_id = 0; |
321 | |
322 | static int |
323 | find_volume_id(struct intel_raid_conf *info) |
324 | { |
325 | int i, ret; |
326 | |
327 | for (i=0; i < n_array; ++i) { |
328 | if (info->checksum == array_note[i].checksum && |
329 | info->config_id == array_note[i].config_id && |
330 | info->generation == array_note[i].generation) { |
331 | /* we have already seen this array */ |
332 | return array_note[i].id; |
333 | } |
334 | } |
335 | |
336 | if (n_array >= __arraycount(array_note)) { |
337 | /* Too many arrays */ |
338 | return -1; |
339 | } |
340 | |
341 | array_note[n_array].checksum = info->checksum; |
342 | array_note[n_array].config_id = info->config_id; |
343 | array_note[n_array].generation = info->generation; |
344 | array_note[n_array].id = ret = volume_id; |
345 | |
346 | /* Allocate volume ids for all volumes in this array */ |
347 | volume_id += info->total_volumes; |
348 | ++n_array; |
349 | return ret; |
350 | } |
351 | |