xref: /freebsd/sys/dev/mps/mps_config.c (revision 685dc743)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2011-2015 LSI Corp.
5  * Copyright (c) 2013-2015 Avago Technologies
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
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 AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * Avago Technologies (LSI) MPT-Fusion Host Adapter FreeBSD
30  */
31 
32 #include <sys/cdefs.h>
33 /* TODO Move headers to mpsvar */
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/lock.h>
37 #include <sys/mutex.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/kthread.h>
42 #include <sys/taskqueue.h>
43 #include <sys/bus.h>
44 #include <sys/endian.h>
45 #include <sys/sysctl.h>
46 #include <sys/eventhandler.h>
47 #include <sys/uio.h>
48 #include <machine/bus.h>
49 #include <machine/resource.h>
50 #include <dev/mps/mpi/mpi2_type.h>
51 #include <dev/mps/mpi/mpi2.h>
52 #include <dev/mps/mpi/mpi2_ioc.h>
53 #include <dev/mps/mpi/mpi2_sas.h>
54 #include <dev/mps/mpi/mpi2_cnfg.h>
55 #include <dev/mps/mpi/mpi2_init.h>
56 #include <dev/mps/mpi/mpi2_tool.h>
57 #include <dev/mps/mps_ioctl.h>
58 #include <dev/mps/mpsvar.h>
59 
60 /**
61  * mps_config_get_ioc_pg8 - obtain ioc page 8
62  * @sc: per adapter object
63  * @mpi_reply: reply mf payload returned from firmware
64  * @config_page: contents of the config page
65  * Context: sleep.
66  *
67  * Returns 0 for success, non-zero for failure.
68  */
69 int
mps_config_get_ioc_pg8(struct mps_softc * sc,Mpi2ConfigReply_t * mpi_reply,Mpi2IOCPage8_t * config_page)70 mps_config_get_ioc_pg8(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
71     Mpi2IOCPage8_t *config_page)
72 {
73 	MPI2_CONFIG_REQUEST *request;
74 	MPI2_CONFIG_REPLY *reply = NULL;
75 	struct mps_command *cm;
76 	MPI2_CONFIG_PAGE_IOC_8 *page = NULL;
77 	int error = 0;
78 	u16 ioc_status;
79 
80 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
81 
82 	if ((cm = mps_alloc_command(sc)) == NULL) {
83 		printf("%s: command alloc failed @ line %d\n", __func__,
84 		    __LINE__);
85 		error = EBUSY;
86 		goto out;
87 	}
88 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
89 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
90 	request->Function = MPI2_FUNCTION_CONFIG;
91 	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
92 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
93 	request->Header.PageNumber = 8;
94 	request->Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
95 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
96 	cm->cm_data = NULL;
97 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
98 	if (cm != NULL)
99 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
100 	if (error || (reply == NULL)) {
101 		/* FIXME */
102 		/*
103 		 * If the request returns an error then we need to do a diag
104 		 * reset
105 		 */
106 		printf("%s: request for header completed with error %d\n",
107 		    __func__, error);
108 		error = ENXIO;
109 		goto out;
110 	}
111 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
112 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
113 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
114 		/* FIXME */
115 		/*
116 		 * If the request returns an error then we need to do a diag
117 		 * reset
118 		 */
119 		printf("%s: header read with error; iocstatus = 0x%x\n",
120 		    __func__, ioc_status);
121 		error = ENXIO;
122 		goto out;
123 	}
124 	/* We have to do free and alloc for the reply-free and reply-post
125 	 * counters to match - Need to review the reply FIFO handling.
126 	 */
127 	mps_free_command(sc, cm);
128 
129 	if ((cm = mps_alloc_command(sc)) == NULL) {
130 		printf("%s: command alloc failed @ line %d\n", __func__,
131 		    __LINE__);
132 		error = EBUSY;
133 		goto out;
134 	}
135 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
136 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
137 	request->Function = MPI2_FUNCTION_CONFIG;
138 	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
139 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_IOC;
140 	request->Header.PageNumber = 8;
141 	request->Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
142 	request->Header.PageLength = mpi_reply->Header.PageLength;
143 	cm->cm_length =  le16toh(mpi_reply->Header.PageLength) * 4;
144 	cm->cm_sge = &request->PageBufferSGE;
145 	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
146 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
147 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
148 	page = malloc((cm->cm_length), M_MPT2, M_ZERO | M_NOWAIT);
149 	if (!page) {
150 		printf("%s: page alloc failed\n", __func__);
151 		error = ENOMEM;
152 		goto out;
153 	}
154 	cm->cm_data = page;
155 
156 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
157 	if (cm != NULL)
158 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
159 	if (error || (reply == NULL)) {
160 		/* FIXME */
161 		/*
162 		 * If the request returns an error then we need to do a diag
163 		 * reset
164 		 */
165 		printf("%s: request for page completed with error %d\n",
166 		    __func__, error);
167 		error = ENXIO;
168 		goto out;
169 	}
170 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
171 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
172 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
173 		/* FIXME */
174 		/*
175 		 * If the request returns an error then we need to do a diag
176 		 * reset
177 		 */
178 		printf("%s: page read with error; iocstatus = 0x%x\n",
179 		    __func__, ioc_status);
180 		error = ENXIO;
181 		goto out;
182 	}
183 	bcopy(page, config_page, MIN(cm->cm_length, (sizeof(Mpi2IOCPage8_t))));
184 
185 out:
186 	free(page, M_MPT2);
187 	if (cm)
188 		mps_free_command(sc, cm);
189 	return (error);
190 }
191 
192 /**
193  * mps_config_get_man_pg10 - obtain Manufacturing Page 10 data and set flags
194  *   accordingly.  Currently, this page does not need to return to caller.
195  * @sc: per adapter object
196  * @mpi_reply: reply mf payload returned from firmware
197  * Context: sleep.
198  *
199  * Returns 0 for success, non-zero for failure.
200  */
201 int
mps_config_get_man_pg10(struct mps_softc * sc,Mpi2ConfigReply_t * mpi_reply)202 mps_config_get_man_pg10(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply)
203 {
204 	MPI2_CONFIG_REQUEST *request;
205 	MPI2_CONFIG_REPLY *reply = NULL;
206 	struct mps_command *cm;
207 	pMpi2ManufacturingPagePS_t page = NULL;
208 	uint32_t *pPS_info;
209 	uint8_t OEM_Value = 0;
210 	int error = 0;
211 	u16 ioc_status;
212 
213 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
214 
215 	if ((cm = mps_alloc_command(sc)) == NULL) {
216 		printf("%s: command alloc failed @ line %d\n", __func__,
217 		    __LINE__);
218 		error = EBUSY;
219 		goto out;
220 	}
221 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
222 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
223 	request->Function = MPI2_FUNCTION_CONFIG;
224 	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
225 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
226 	request->Header.PageNumber = 10;
227 	request->Header.PageVersion = MPI2_MANUFACTURING10_PAGEVERSION;
228 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
229 	cm->cm_data = NULL;
230 
231 	/*
232 	 * This page must be polled because the IOC isn't ready yet when this
233 	 * page is needed.
234 	 */
235 	error = mps_wait_command(sc, &cm, 60, 0);
236 	if (cm != NULL)
237 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
238 	if (error || (reply == NULL)) {
239 		/* FIXME */
240 		/* If the poll returns error then we need to do diag reset */
241 		printf("%s: poll for header completed with error %d\n",
242 		    __func__, error);
243 		error = ENXIO;
244 		goto out;
245 	}
246 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
247 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
248 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
249 		/* FIXME */
250 		/* If the poll returns error then we need to do diag reset */
251 		printf("%s: header read with error; iocstatus = 0x%x\n",
252 		    __func__, ioc_status);
253 		error = ENXIO;
254 		goto out;
255 	}
256 	/* We have to do free and alloc for the reply-free and reply-post
257 	 * counters to match - Need to review the reply FIFO handling.
258 	 */
259 	mps_free_command(sc, cm);
260 
261 	if ((cm = mps_alloc_command(sc)) == NULL) {
262 		printf("%s: command alloc failed @ line %d\n", __func__,
263 		    __LINE__);
264 		error = EBUSY;
265 		goto out;
266 	}
267 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
268 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
269 	request->Function = MPI2_FUNCTION_CONFIG;
270 	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
271 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
272 	request->Header.PageNumber = 10;
273 	request->Header.PageVersion = MPI2_MANUFACTURING10_PAGEVERSION;
274 	request->Header.PageLength = mpi_reply->Header.PageLength;
275 	cm->cm_length =  le16toh(mpi_reply->Header.PageLength) * 4;
276 	cm->cm_sge = &request->PageBufferSGE;
277 	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
278 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
279 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
280 	page = malloc(MPS_MAN_PAGE10_SIZE, M_MPT2, M_ZERO | M_NOWAIT);
281 	if (!page) {
282 		printf("%s: page alloc failed\n", __func__);
283 		error = ENOMEM;
284 		goto out;
285 	}
286 	cm->cm_data = page;
287 
288 	/*
289 	 * This page must be polled because the IOC isn't ready yet when this
290 	 * page is needed.
291 	 */
292 	error = mps_wait_command(sc, &cm, 60, 0);
293 	if (cm != NULL)
294 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
295 	if (error || (reply == NULL)) {
296 		/* FIXME */
297 		/* If the poll returns error then we need to do diag reset */
298 		printf("%s: poll for page completed with error %d\n",
299 		    __func__, error);
300 		error = ENXIO;
301 		goto out;
302 	}
303 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
304 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
305 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
306 		/* FIXME */
307 		/* If the poll returns error then we need to do diag reset */
308 		printf("%s: page read with error; iocstatus = 0x%x\n",
309 		    __func__, ioc_status);
310 		error = ENXIO;
311 		goto out;
312 	}
313 
314 	/*
315 	 * If OEM ID is unknown, fail the request.
316 	 */
317 	sc->WD_hide_expose = MPS_WD_HIDE_ALWAYS;
318 	OEM_Value = (uint8_t)(page->ProductSpecificInfo & 0x000000FF);
319 	if (OEM_Value != MPS_WD_LSI_OEM) {
320 		mps_dprint(sc, MPS_FAULT, "Unknown OEM value for WarpDrive "
321 		    "(0x%x)\n", OEM_Value);
322 		error = ENXIO;
323 		goto out;
324 	}
325 
326 	/*
327 	 * Set the phys disks hide/expose value.
328 	 */
329 	pPS_info = &page->ProductSpecificInfo;
330 	sc->WD_hide_expose = (uint8_t)(pPS_info[5]);
331 	sc->WD_hide_expose &= MPS_WD_HIDE_EXPOSE_MASK;
332 	if ((sc->WD_hide_expose != MPS_WD_HIDE_ALWAYS) &&
333 	    (sc->WD_hide_expose != MPS_WD_EXPOSE_ALWAYS) &&
334 	    (sc->WD_hide_expose != MPS_WD_HIDE_IF_VOLUME)) {
335 		mps_dprint(sc, MPS_FAULT, "Unknown value for WarpDrive "
336 		    "hide/expose: 0x%x\n", sc->WD_hide_expose);
337 		error = ENXIO;
338 		goto out;
339 	}
340 
341 out:
342 	free(page, M_MPT2);
343 	if (cm)
344 		mps_free_command(sc, cm);
345 	return (error);
346 }
347 
348 /**
349  * mps_base_static_config_pages - static start of day config pages.
350  * @sc: per adapter object
351  *
352  * Return nothing.
353  */
354 void
mps_base_static_config_pages(struct mps_softc * sc)355 mps_base_static_config_pages(struct mps_softc *sc)
356 {
357 	Mpi2ConfigReply_t	mpi_reply;
358 	int			retry;
359 
360 	retry = 0;
361 	while (mps_config_get_ioc_pg8(sc, &mpi_reply, &sc->ioc_pg8)) {
362 		retry++;
363 		if (retry > 5) {
364 			/* We need to Handle this situation */
365 			/*FIXME*/
366 			break;
367 		}
368 	}
369 }
370 
371 /**
372  * mps_wd_config_pages - get info required to support WarpDrive.  This needs to
373  *    be called after discovery is complete to guarantee that IR info is there.
374  * @sc: per adapter object
375  *
376  * Return nothing.
377  */
378 void
mps_wd_config_pages(struct mps_softc * sc)379 mps_wd_config_pages(struct mps_softc *sc)
380 {
381 	Mpi2ConfigReply_t	mpi_reply;
382 	pMpi2RaidVolPage0_t	raid_vol_pg0 = NULL;
383 	Mpi2RaidPhysDiskPage0_t	phys_disk_pg0;
384 	pMpi2RaidVol0PhysDisk_t	pRVPD;
385 	uint32_t		stripe_size, phys_disk_page_address;
386 	uint16_t		block_size;
387 	uint8_t			index, stripe_exp = 0, block_exp = 0;
388 
389 	/*
390 	 * Get the WD settings from manufacturing page 10 if using a WD HBA.
391 	 * This will be used to determine if phys disks should always be
392 	 * hidden, hidden only if part of a WD volume, or never hidden.  Also,
393 	 * get the WD RAID Volume info and fail if volume does not exist or if
394 	 * volume does not meet the requirements for a WD volume.  No retry
395 	 * here.  Just default to HIDE ALWAYS if man Page10 fails, or clear WD
396 	 * Valid flag if Volume info fails.
397 	 */
398 	sc->WD_valid_config = FALSE;
399 	if (sc->mps_flags & MPS_FLAGS_WD_AVAILABLE) {
400 		if (mps_config_get_man_pg10(sc, &mpi_reply)) {
401 			mps_dprint(sc, MPS_FAULT,
402 			    "mps_config_get_man_pg10 failed! Using 0 (Hide "
403 			    "Always) for WarpDrive hide/expose value.\n");
404 			sc->WD_hide_expose = MPS_WD_HIDE_ALWAYS;
405 		}
406 
407 		/*
408 		 * Get first RAID Volume Page0 using GET_NEXT_HANDLE.
409 		 */
410 		raid_vol_pg0 = malloc(sizeof(Mpi2RaidVolPage0_t) +
411 		    (sizeof(Mpi2RaidVol0PhysDisk_t) * MPS_MAX_DISKS_IN_VOL),
412 		    M_MPT2, M_ZERO | M_NOWAIT);
413 		if (!raid_vol_pg0) {
414 			printf("%s: page alloc failed\n", __func__);
415 			goto out;
416 		}
417 
418 		if (mps_config_get_raid_volume_pg0(sc, &mpi_reply, raid_vol_pg0,
419 		    0x0000FFFF)) {
420 			mps_dprint(sc, MPS_INFO,
421 			    "mps_config_get_raid_volume_pg0 failed! Assuming "
422 			    "WarpDrive IT mode.\n");
423 			goto out;
424 		}
425 
426 		/*
427 		 * Check for valid WD configuration:
428 		 *   volume type is RAID0
429 		 *   number of phys disks in the volume is no more than 8
430 		 */
431 		if ((raid_vol_pg0->VolumeType != MPI2_RAID_VOL_TYPE_RAID0) ||
432 		    (raid_vol_pg0->NumPhysDisks > 8)) {
433 			mps_dprint(sc, MPS_FAULT,
434 			    "Invalid WarpDrive configuration. Direct Drive I/O "
435 			    "will not be used.\n");
436 			goto out;
437 		}
438 
439 		/*
440 		 * Save the WD RAID data to be used during WD I/O.
441 		 */
442 		sc->DD_max_lba = le64toh((uint64_t)raid_vol_pg0->MaxLBA.High <<
443 		    32 | (uint64_t)raid_vol_pg0->MaxLBA.Low);
444 		sc->DD_num_phys_disks = raid_vol_pg0->NumPhysDisks;
445 		sc->DD_dev_handle = raid_vol_pg0->DevHandle;
446 		sc->DD_stripe_size = raid_vol_pg0->StripeSize;
447 		sc->DD_block_size = raid_vol_pg0->BlockSize;
448 
449 		/*
450 		 * Find power of 2 of stripe size and set this as the exponent.
451 		 * Fail if stripe size is 0.
452 		 */
453 		stripe_size = raid_vol_pg0->StripeSize;
454 		for (index = 0; index < 32; index++) {
455 			if (stripe_size & 1)
456 				break;
457 			stripe_exp++;
458 			stripe_size >>= 1;
459 		}
460 		if (index == 32) {
461 			mps_dprint(sc, MPS_FAULT,
462 			    "RAID Volume's stripe size is 0. Direct Drive I/O "
463 			    "will not be used.\n");
464 			goto out;
465 		}
466 		sc->DD_stripe_exponent = stripe_exp;
467 
468 		/*
469 		 * Find power of 2 of block size and set this as the exponent.
470 		 * Fail if block size is 0.
471 		 */
472 		block_size = raid_vol_pg0->BlockSize;
473 		for (index = 0; index < 16; index++) {
474 			if (block_size & 1)
475 				break;
476 			block_exp++;
477 			block_size >>= 1;
478 		}
479 		if (index == 16) {
480 			mps_dprint(sc, MPS_FAULT,
481 			    "RAID Volume's block size is 0. Direct Drive I/O "
482 			    "will not be used.\n");
483 			goto out;
484 		}
485 		sc->DD_block_exponent = block_exp;
486 
487 		/*
488 		 * Loop through all of the volume's Phys Disks to map the phys
489 		 * disk number into the columm map.  This is used during Direct
490 		 * Drive I/O to send the request to the correct SSD.
491 		 */
492 		pRVPD = (pMpi2RaidVol0PhysDisk_t)&raid_vol_pg0->PhysDisk;
493 		for (index = 0; index < raid_vol_pg0->NumPhysDisks; index++) {
494 			sc->DD_column_map[pRVPD->PhysDiskMap].phys_disk_num =
495 			    pRVPD->PhysDiskNum;
496 			pRVPD++;
497 		}
498 
499 		/*
500 		 * Get second RAID Volume Page0 using previous handle.  This
501 		 * page should not exist.  If it does, must not proceed with WD
502 		 * handling.
503 		 */
504 		if (mps_config_get_raid_volume_pg0(sc, &mpi_reply,
505 		    raid_vol_pg0, (u32)raid_vol_pg0->DevHandle)) {
506 			if ((le16toh(mpi_reply.IOCStatus) &
507 			    MPI2_IOCSTATUS_MASK) !=
508 			    MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) {
509 				mps_dprint(sc, MPS_FAULT,
510 				    "Multiple RAID Volume Page0! Direct Drive "
511 				    "I/O will not be used.\n");
512 				goto out;
513 			}
514 		} else {
515 			mps_dprint(sc, MPS_FAULT,
516 			    "Multiple volumes! Direct Drive I/O will not be "
517 			    "used.\n");
518 			goto out;
519 		}
520 
521 		/*
522 		 * Get RAID Volume Phys Disk Page 0 for all SSDs in the volume.
523 		 */
524 		for (index = 0; index < raid_vol_pg0->NumPhysDisks; index++) {
525 			phys_disk_page_address =
526 			    MPI2_PHYSDISK_PGAD_FORM_PHYSDISKNUM +
527 			    sc->DD_column_map[index].phys_disk_num;
528 			if (mps_config_get_raid_pd_pg0(sc, &mpi_reply,
529 			    &phys_disk_pg0, phys_disk_page_address)) {
530 				mps_dprint(sc, MPS_FAULT,
531 				    "mps_config_get_raid_pd_pg0 failed! Direct "
532 				    "Drive I/O will not be used.\n");
533 				goto out;
534 			}
535 			if (phys_disk_pg0.DevHandle == 0xFFFF) {
536 				mps_dprint(sc, MPS_FAULT,
537 				    "Invalid Phys Disk DevHandle! Direct Drive "
538 				    "I/O will not be used.\n");
539 				goto out;
540 			}
541 			sc->DD_column_map[index].dev_handle =
542 			    phys_disk_pg0.DevHandle;
543 		}
544 		sc->WD_valid_config = TRUE;
545 out:
546 		if (raid_vol_pg0)
547 			free(raid_vol_pg0, M_MPT2);
548 	}
549 }
550 
551 /**
552  * mps_config_get_dpm_pg0 - obtain driver persistent mapping page0
553  * @sc: per adapter object
554  * @mpi_reply: reply mf payload returned from firmware
555  * @config_page: contents of the config page
556  * @sz: size of buffer passed in config_page
557  * Context: sleep.
558  *
559  * Returns 0 for success, non-zero for failure.
560  */
561 int
mps_config_get_dpm_pg0(struct mps_softc * sc,Mpi2ConfigReply_t * mpi_reply,Mpi2DriverMappingPage0_t * config_page,u16 sz)562 mps_config_get_dpm_pg0(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
563     Mpi2DriverMappingPage0_t *config_page, u16 sz)
564 {
565 	MPI2_CONFIG_REQUEST *request;
566 	MPI2_CONFIG_REPLY *reply = NULL;
567 	struct mps_command *cm;
568 	Mpi2DriverMappingPage0_t *page = NULL;
569 	int error = 0;
570 	u16 ioc_status;
571 
572 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
573 
574 	memset(config_page, 0, sz);
575 	if ((cm = mps_alloc_command(sc)) == NULL) {
576 		printf("%s: command alloc failed @ line %d\n", __func__,
577 		    __LINE__);
578 		error = EBUSY;
579 		goto out;
580 	}
581 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
582 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
583 	request->Function = MPI2_FUNCTION_CONFIG;
584 	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
585 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
586 	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
587 	request->Header.PageNumber = 0;
588 	request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
589 	request->PageAddress = sc->max_dpm_entries <<
590 	    MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
591 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
592 	cm->cm_data = NULL;
593 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
594 	if (cm != NULL)
595 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
596 	if (error || (reply == NULL)) {
597 		/* FIXME */
598 		/*
599 		 * If the request returns an error then we need to do a diag
600 		 * reset
601 		 */
602 		printf("%s: request for header completed with error %d\n",
603 		    __func__, error);
604 		error = ENXIO;
605 		goto out;
606 	}
607 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
608 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
609 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
610 		/* FIXME */
611 		/*
612 		 * If the request returns an error then we need to do a diag
613 		 * reset
614 		 */
615 		printf("%s: header read with error; iocstatus = 0x%x\n",
616 		    __func__, ioc_status);
617 		error = ENXIO;
618 		goto out;
619 	}
620 	/* We have to do free and alloc for the reply-free and reply-post
621 	 * counters to match - Need to review the reply FIFO handling.
622 	 */
623 	mps_free_command(sc, cm);
624 
625 	if ((cm = mps_alloc_command(sc)) == NULL) {
626 		printf("%s: command alloc failed @ line %d\n", __func__,
627 		    __LINE__);
628 		error = EBUSY;
629 		goto out;
630 	}
631 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
632 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
633 	request->Function = MPI2_FUNCTION_CONFIG;
634 	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_NVRAM;
635 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
636 	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
637 	request->Header.PageNumber = 0;
638 	request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
639 	request->PageAddress = sc->max_dpm_entries <<
640 	    MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
641 	request->ExtPageLength = mpi_reply->ExtPageLength;
642 	cm->cm_length =  le16toh(request->ExtPageLength) * 4;
643 	cm->cm_sge = &request->PageBufferSGE;
644 	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
645 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
646 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
647 	page = malloc(cm->cm_length, M_MPT2, M_ZERO|M_NOWAIT);
648 	if (!page) {
649 		printf("%s: page alloc failed\n", __func__);
650 		error = ENOMEM;
651 		goto out;
652 	}
653 	cm->cm_data = page;
654 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
655 	if (cm != NULL)
656 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
657 	if (error || (reply == NULL)) {
658 		/* FIXME */
659 		/*
660 		 * If the request returns an error then we need to do a diag
661 		 * reset
662 		 */
663 		printf("%s: request for page completed with error %d\n",
664 		    __func__, error);
665 		error = ENXIO;
666 		goto out;
667 	}
668 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
669 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
670 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
671 		/* FIXME */
672 		/*
673 		 * If the request returns an error then we need to do a diag
674 		 * reset
675 		 */
676 		printf("%s: page read with error; iocstatus = 0x%x\n",
677 		    __func__, ioc_status);
678 		error = ENXIO;
679 		goto out;
680 	}
681 	bcopy(page, config_page, MIN(cm->cm_length, sz));
682 out:
683 	free(page, M_MPT2);
684 	if (cm)
685 		mps_free_command(sc, cm);
686 	return (error);
687 }
688 
689 /**
690  * mps_config_set_dpm_pg0 - write an entry in driver persistent mapping page0
691  * @sc: per adapter object
692  * @mpi_reply: reply mf payload returned from firmware
693  * @config_page: contents of the config page
694  * @entry_idx: entry index in DPM Page0 to be modified
695  * Context: sleep.
696  *
697  * Returns 0 for success, non-zero for failure.
698  */
699 
mps_config_set_dpm_pg0(struct mps_softc * sc,Mpi2ConfigReply_t * mpi_reply,Mpi2DriverMappingPage0_t * config_page,u16 entry_idx)700 int mps_config_set_dpm_pg0(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
701     Mpi2DriverMappingPage0_t *config_page, u16 entry_idx)
702 {
703 	MPI2_CONFIG_REQUEST *request;
704 	MPI2_CONFIG_REPLY *reply = NULL;
705 	struct mps_command *cm;
706 	MPI2_CONFIG_PAGE_DRIVER_MAPPING_0 *page = NULL;
707 	int error = 0;
708 	u16 ioc_status;
709 
710 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
711 
712 	if ((cm = mps_alloc_command(sc)) == NULL) {
713 		printf("%s: command alloc failed @ line %d\n", __func__,
714 		    __LINE__);
715 		error = EBUSY;
716 		goto out;
717 	}
718 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
719 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
720 	request->Function = MPI2_FUNCTION_CONFIG;
721 	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
722 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
723 	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
724 	request->Header.PageNumber = 0;
725 	request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
726 	/* We can remove below two lines ????*/
727 	request->PageAddress = 1 << MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
728 	request->PageAddress |= htole16(entry_idx);
729 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
730 	cm->cm_data = NULL;
731 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
732 	if (cm != NULL)
733 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
734 	if (error || (reply == NULL)) {
735 		/* FIXME */
736 		/*
737 		 * If the request returns an error then we need to do a diag
738 		 * reset
739 		 */
740 		printf("%s: request for header completed with error %d\n",
741 		    __func__, error);
742 		error = ENXIO;
743 		goto out;
744 	}
745 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
746 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
747 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
748 		/* FIXME */
749 		/*
750 		 * If the request returns an error then we need to do a diag
751 		 * reset
752 		 */
753 		printf("%s: header read with error; iocstatus = 0x%x\n",
754 		    __func__, ioc_status);
755 		error = ENXIO;
756 		goto out;
757 	}
758 	/* We have to do free and alloc for the reply-free and reply-post
759 	 * counters to match - Need to review the reply FIFO handling.
760 	 */
761 	mps_free_command(sc, cm);
762 
763 	if ((cm = mps_alloc_command(sc)) == NULL) {
764 		printf("%s: command alloc failed @ line %d\n", __func__,
765 		    __LINE__);
766 		error = EBUSY;
767 		goto out;
768 	}
769 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
770 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
771 	request->Function = MPI2_FUNCTION_CONFIG;
772 	request->Action = MPI2_CONFIG_ACTION_PAGE_WRITE_NVRAM;
773 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
774 	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_DRIVER_MAPPING;
775 	request->Header.PageNumber = 0;
776 	request->Header.PageVersion = MPI2_DRIVERMAPPING0_PAGEVERSION;
777 	request->ExtPageLength = mpi_reply->ExtPageLength;
778 	request->PageAddress = 1 << MPI2_DPM_PGAD_ENTRY_COUNT_SHIFT;
779 	request->PageAddress |= htole16(entry_idx);
780 	cm->cm_length = le16toh(mpi_reply->ExtPageLength) * 4;
781 	cm->cm_sge = &request->PageBufferSGE;
782 	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
783 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAOUT;
784 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
785 	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
786 	if (!page) {
787 		printf("%s: page alloc failed\n", __func__);
788 		error = ENOMEM;
789 		goto out;
790 	}
791 	bcopy(config_page, page, MIN(cm->cm_length,
792 	    (sizeof(Mpi2DriverMappingPage0_t))));
793 	cm->cm_data = page;
794 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
795 	if (cm != NULL)
796 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
797 	if (error || (reply == NULL)) {
798 		/* FIXME */
799 		/*
800 		 * If the request returns an error then we need to do a diag
801 		 * reset
802 		 */
803 		printf("%s: request to write page completed with error %d\n",
804 		    __func__, error);
805 		error = ENXIO;
806 		goto out;
807 	}
808 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
809 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
810 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
811 		/* FIXME */
812 		/*
813 		 * If the request returns an error then we need to do a diag
814 		 * reset
815 		 */
816 		printf("%s: page written with error; iocstatus = 0x%x\n",
817 		    __func__, ioc_status);
818 		error = ENXIO;
819 		goto out;
820 	}
821 out:
822 	free(page, M_MPT2);
823 	if (cm)
824 		mps_free_command(sc, cm);
825 	return (error);
826 }
827 
828 /**
829  * mps_config_get_sas_device_pg0 - obtain sas device page 0
830  * @sc: per adapter object
831  * @mpi_reply: reply mf payload returned from firmware
832  * @config_page: contents of the config page
833  * @form: GET_NEXT_HANDLE or HANDLE
834  * @handle: device handle
835  * Context: sleep.
836  *
837  * Returns 0 for success, non-zero for failure.
838  */
839 int
mps_config_get_sas_device_pg0(struct mps_softc * sc,Mpi2ConfigReply_t * mpi_reply,Mpi2SasDevicePage0_t * config_page,u32 form,u16 handle)840 mps_config_get_sas_device_pg0(struct mps_softc *sc, Mpi2ConfigReply_t
841     *mpi_reply, Mpi2SasDevicePage0_t *config_page, u32 form, u16 handle)
842 {
843 	MPI2_CONFIG_REQUEST *request;
844 	MPI2_CONFIG_REPLY *reply = NULL;
845 	struct mps_command *cm;
846 	Mpi2SasDevicePage0_t *page = NULL;
847 	int error = 0;
848 	u16 ioc_status;
849 
850 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
851 
852 	if ((cm = mps_alloc_command(sc)) == NULL) {
853 		printf("%s: command alloc failed @ line %d\n", __func__,
854 		    __LINE__);
855 		error = EBUSY;
856 		goto out;
857 	}
858 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
859 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
860 	request->Function = MPI2_FUNCTION_CONFIG;
861 	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
862 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
863 	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
864 	request->Header.PageNumber = 0;
865 	request->Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION;
866 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
867 	cm->cm_data = NULL;
868 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
869 	if (cm != NULL)
870 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
871 	if (error || (reply == NULL)) {
872 		/* FIXME */
873 		/*
874 		 * If the request returns an error then we need to do a diag
875 		 * reset
876 		 */
877 		printf("%s: request for header completed with error %d\n",
878 		    __func__, error);
879 		error = ENXIO;
880 		goto out;
881 	}
882 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
883 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
884 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
885 		/* FIXME */
886 		/*
887 		 * If the request returns an error then we need to do a diag
888 		 * reset
889 		 */
890 		printf("%s: header read with error; iocstatus = 0x%x\n",
891 		    __func__, ioc_status);
892 		error = ENXIO;
893 		goto out;
894 	}
895 	/* We have to do free and alloc for the reply-free and reply-post
896 	 * counters to match - Need to review the reply FIFO handling.
897 	 */
898 	mps_free_command(sc, cm);
899 
900 	if ((cm = mps_alloc_command(sc)) == NULL) {
901 		printf("%s: command alloc failed @ line %d\n", __func__,
902 		    __LINE__);
903 		error = EBUSY;
904 		goto out;
905 	}
906 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
907 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
908 	request->Function = MPI2_FUNCTION_CONFIG;
909 	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
910 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_EXTENDED;
911 	request->ExtPageType = MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE;
912 	request->Header.PageNumber = 0;
913 	request->Header.PageVersion = MPI2_SASDEVICE0_PAGEVERSION;
914 	request->ExtPageLength = mpi_reply->ExtPageLength;
915 	request->PageAddress = htole32(form | handle);
916 	cm->cm_length = le16toh(mpi_reply->ExtPageLength) * 4;
917 	cm->cm_sge = &request->PageBufferSGE;
918 	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
919 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
920 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
921 	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
922 	if (!page) {
923 		printf("%s: page alloc failed\n", __func__);
924 		error = ENOMEM;
925 		goto out;
926 	}
927 	cm->cm_data = page;
928 
929 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
930 	if (cm != NULL)
931 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
932 	if (error || (reply == NULL)) {
933 		/* FIXME */
934 		/*
935 		 * If the request returns an error then we need to do a diag
936 		 * reset
937 		 */
938 		printf("%s: request for page completed with error %d\n",
939 		    __func__, error);
940 		error = ENXIO;
941 		goto out;
942 	}
943 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
944 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
945 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
946 		/* FIXME */
947 		/*
948 		 * If the request returns an error then we need to do a diag
949 		 * reset
950 		 */
951 		printf("%s: page read with error; iocstatus = 0x%x\n",
952 		    __func__, ioc_status);
953 		error = ENXIO;
954 		goto out;
955 	}
956 	bcopy(page, config_page, MIN(cm->cm_length,
957 	    sizeof(Mpi2SasDevicePage0_t)));
958 out:
959 	free(page, M_MPT2);
960 	if (cm)
961 		mps_free_command(sc, cm);
962 	return (error);
963 }
964 
965 /**
966  * mps_config_get_bios_pg3 - obtain BIOS page 3
967  * @sc: per adapter object
968  * @mpi_reply: reply mf payload returned from firmware
969  * @config_page: contents of the config page
970  * Context: sleep.
971  *
972  * Returns 0 for success, non-zero for failure.
973  */
974 int
mps_config_get_bios_pg3(struct mps_softc * sc,Mpi2ConfigReply_t * mpi_reply,Mpi2BiosPage3_t * config_page)975 mps_config_get_bios_pg3(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
976     Mpi2BiosPage3_t *config_page)
977 {
978 	MPI2_CONFIG_REQUEST *request;
979 	MPI2_CONFIG_REPLY *reply = NULL;
980 	struct mps_command *cm;
981 	Mpi2BiosPage3_t *page = NULL;
982 	int error = 0;
983 	u16 ioc_status;
984 
985 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
986 
987 	if ((cm = mps_alloc_command(sc)) == NULL) {
988 		printf("%s: command alloc failed @ line %d\n", __func__,
989 		    __LINE__);
990 		error = EBUSY;
991 		goto out;
992 	}
993 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
994 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
995 	request->Function = MPI2_FUNCTION_CONFIG;
996 	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
997 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
998 	request->Header.PageNumber = 3;
999 	request->Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
1000 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
1001 	cm->cm_data = NULL;
1002 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
1003 	if (cm != NULL)
1004 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
1005 	if (error || (reply == NULL)) {
1006 		/* FIXME */
1007 		/*
1008 		 * If the request returns an error then we need to do a diag
1009 		 * reset
1010 		 */
1011 		printf("%s: request for header completed with error %d\n",
1012 		    __func__, error);
1013 		error = ENXIO;
1014 		goto out;
1015 	}
1016 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
1017 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
1018 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
1019 		/* FIXME */
1020 		/*
1021 		 * If the request returns an error then we need to do a diag
1022 		 * reset
1023 		 */
1024 		printf("%s: header read with error; iocstatus = 0x%x\n",
1025 		    __func__, ioc_status);
1026 		error = ENXIO;
1027 		goto out;
1028 	}
1029 	/* We have to do free and alloc for the reply-free and reply-post
1030 	 * counters to match - Need to review the reply FIFO handling.
1031 	 */
1032 	mps_free_command(sc, cm);
1033 
1034 	if ((cm = mps_alloc_command(sc)) == NULL) {
1035 		printf("%s: command alloc failed @ line %d\n", __func__,
1036 		    __LINE__);
1037 		error = EBUSY;
1038 		goto out;
1039 	}
1040 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
1041 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
1042 	request->Function = MPI2_FUNCTION_CONFIG;
1043 	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
1044 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_BIOS;
1045 	request->Header.PageNumber = 3;
1046 	request->Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
1047 	request->Header.PageLength = mpi_reply->Header.PageLength;
1048 	cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
1049 	cm->cm_sge = &request->PageBufferSGE;
1050 	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
1051 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
1052 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
1053 	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
1054 	if (!page) {
1055 		printf("%s: page alloc failed\n", __func__);
1056 		error = ENOMEM;
1057 		goto out;
1058 	}
1059 	cm->cm_data = page;
1060 
1061 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
1062 	if (cm != NULL)
1063 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
1064 	if (error || (reply == NULL)) {
1065 		/* FIXME */
1066 		/*
1067 		 * If the request returns an error then we need to do a diag
1068 		 * reset
1069 		 */
1070 		printf("%s: request for page completed with error %d\n",
1071 		    __func__, error);
1072 		error = ENXIO;
1073 		goto out;
1074 	}
1075 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
1076 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
1077 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
1078 		/* FIXME */
1079 		/*
1080 		 * If the request returns an error then we need to do a diag
1081 		 * reset
1082 		 */
1083 		printf("%s: page read with error; iocstatus = 0x%x\n",
1084 		    __func__, ioc_status);
1085 		error = ENXIO;
1086 		goto out;
1087 	}
1088 	bcopy(page, config_page, MIN(cm->cm_length, sizeof(Mpi2BiosPage3_t)));
1089 out:
1090 	free(page, M_MPT2);
1091 	if (cm)
1092 		mps_free_command(sc, cm);
1093 	return (error);
1094 }
1095 
1096 /**
1097  * mps_config_get_raid_volume_pg0 - obtain raid volume page 0
1098  * @sc: per adapter object
1099  * @mpi_reply: reply mf payload returned from firmware
1100  * @config_page: contents of the config page
1101  * @page_address: form and handle value used to get page
1102  * Context: sleep.
1103  *
1104  * Returns 0 for success, non-zero for failure.
1105  */
1106 int
mps_config_get_raid_volume_pg0(struct mps_softc * sc,Mpi2ConfigReply_t * mpi_reply,Mpi2RaidVolPage0_t * config_page,u32 page_address)1107 mps_config_get_raid_volume_pg0(struct mps_softc *sc, Mpi2ConfigReply_t
1108     *mpi_reply, Mpi2RaidVolPage0_t *config_page, u32 page_address)
1109 {
1110 	MPI2_CONFIG_REQUEST *request;
1111 	MPI2_CONFIG_REPLY *reply = NULL;
1112 	struct mps_command *cm;
1113 	Mpi2RaidVolPage0_t *page = NULL;
1114 	int error = 0;
1115 	u16 ioc_status;
1116 
1117 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
1118 
1119 	if ((cm = mps_alloc_command(sc)) == NULL) {
1120 		printf("%s: command alloc failed @ line %d\n", __func__,
1121 		    __LINE__);
1122 		error = EBUSY;
1123 		goto out;
1124 	}
1125 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
1126 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
1127 	request->Function = MPI2_FUNCTION_CONFIG;
1128 	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
1129 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
1130 	request->Header.PageNumber = 0;
1131 	request->Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
1132 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
1133 	cm->cm_data = NULL;
1134 
1135 	/*
1136 	 * This page must be polled because the IOC isn't ready yet when this
1137 	 * page is needed.
1138 	 */
1139 	error = mps_wait_command(sc, &cm, 60, 0);
1140 	if (cm != NULL)
1141 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
1142 	if (error || (reply == NULL)) {
1143 		/* FIXME */
1144 		/* If the poll returns error then we need to do diag reset */
1145 		printf("%s: poll for header completed with error %d\n",
1146 		    __func__, error);
1147 		error = ENXIO;
1148 		goto out;
1149 	}
1150 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
1151 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
1152 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
1153 		/* FIXME */
1154 		/* If the poll returns error then we need to do diag reset */
1155 		printf("%s: header read with error; iocstatus = 0x%x\n",
1156 		    __func__, ioc_status);
1157 		error = ENXIO;
1158 		goto out;
1159 	}
1160 	/* We have to do free and alloc for the reply-free and reply-post
1161 	 * counters to match - Need to review the reply FIFO handling.
1162 	 */
1163 	mps_free_command(sc, cm);
1164 
1165 	if ((cm = mps_alloc_command(sc)) == NULL) {
1166 		printf("%s: command alloc failed @ line %d\n", __func__,
1167 		    __LINE__);
1168 		error = EBUSY;
1169 		goto out;
1170 	}
1171 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
1172 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
1173 	request->Function = MPI2_FUNCTION_CONFIG;
1174 	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
1175 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
1176 	request->Header.PageNumber = 0;
1177 	request->Header.PageLength = mpi_reply->Header.PageLength;
1178 	request->Header.PageVersion = mpi_reply->Header.PageVersion;
1179 	request->PageAddress = page_address;
1180 	cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
1181 	cm->cm_sge = &request->PageBufferSGE;
1182 	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
1183 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
1184 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
1185 	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
1186 	if (!page) {
1187 		printf("%s: page alloc failed\n", __func__);
1188 		error = ENOMEM;
1189 		goto out;
1190 	}
1191 	cm->cm_data = page;
1192 
1193 	/*
1194 	 * This page must be polled because the IOC isn't ready yet when this
1195 	 * page is needed.
1196 	 */
1197 	error = mps_wait_command(sc, &cm, 60, 0);
1198 	if (cm != NULL)
1199 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
1200 	if (error || (reply == NULL)) {
1201 		/* FIXME */
1202 		/* If the poll returns error then we need to do diag reset */
1203 		printf("%s: poll for page completed with error %d\n",
1204 		    __func__, error);
1205 		error = ENXIO;
1206 		goto out;
1207 	}
1208 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
1209 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
1210 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
1211 		/* FIXME */
1212 		/* If the poll returns error then we need to do diag reset */
1213 		printf("%s: page read with error; iocstatus = 0x%x\n",
1214 		    __func__, ioc_status);
1215 		error = ENXIO;
1216 		goto out;
1217 	}
1218 	bcopy(page, config_page, cm->cm_length);
1219 out:
1220 	free(page, M_MPT2);
1221 	if (cm)
1222 		mps_free_command(sc, cm);
1223 	return (error);
1224 }
1225 
1226 /**
1227  * mps_config_get_raid_volume_pg1 - obtain raid volume page 1
1228  * @sc: per adapter object
1229  * @mpi_reply: reply mf payload returned from firmware
1230  * @config_page: contents of the config page
1231  * @form: GET_NEXT_HANDLE or HANDLE
1232  * @handle: volume handle
1233  * Context: sleep.
1234  *
1235  * Returns 0 for success, non-zero for failure.
1236  */
1237 int
mps_config_get_raid_volume_pg1(struct mps_softc * sc,Mpi2ConfigReply_t * mpi_reply,Mpi2RaidVolPage1_t * config_page,u32 form,u16 handle)1238 mps_config_get_raid_volume_pg1(struct mps_softc *sc, Mpi2ConfigReply_t
1239     *mpi_reply, Mpi2RaidVolPage1_t *config_page, u32 form, u16 handle)
1240 {
1241 	MPI2_CONFIG_REQUEST *request;
1242 	MPI2_CONFIG_REPLY *reply = NULL;
1243 	struct mps_command *cm;
1244 	Mpi2RaidVolPage1_t *page = NULL;
1245 	int error = 0;
1246 	u16 ioc_status;
1247 
1248 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
1249 
1250 	if ((cm = mps_alloc_command(sc)) == NULL) {
1251 		printf("%s: command alloc failed @ line %d\n", __func__,
1252 		    __LINE__);
1253 		error = EBUSY;
1254 		goto out;
1255 	}
1256 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
1257 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
1258 	request->Function = MPI2_FUNCTION_CONFIG;
1259 	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
1260 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
1261 	request->Header.PageNumber = 1;
1262 	request->Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION;
1263 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
1264 	cm->cm_data = NULL;
1265 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
1266 	if (cm != NULL)
1267 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
1268 	if (error || (reply == NULL)) {
1269 		/* FIXME */
1270 		/*
1271 		 * If the request returns an error then we need to do a diag
1272 		 * reset
1273 		 */
1274 		printf("%s: request for header completed with error %d\n",
1275 		    __func__, error);
1276 		error = ENXIO;
1277 		goto out;
1278 	}
1279 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
1280 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
1281 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
1282 		/* FIXME */
1283 		/*
1284 		 * If the request returns an error then we need to do a diag
1285 		 * reset
1286 		 */
1287 		printf("%s: header read with error; iocstatus = 0x%x\n",
1288 		    __func__, ioc_status);
1289 		error = ENXIO;
1290 		goto out;
1291 	}
1292 	/* We have to do free and alloc for the reply-free and reply-post
1293 	 * counters to match - Need to review the reply FIFO handling.
1294 	 */
1295 	mps_free_command(sc, cm);
1296 
1297 	if ((cm = mps_alloc_command(sc)) == NULL) {
1298 		printf("%s: command alloc failed @ line %d\n", __func__,
1299 		    __LINE__);
1300 		error = EBUSY;
1301 		goto out;
1302 	}
1303 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
1304 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
1305 	request->Function = MPI2_FUNCTION_CONFIG;
1306 	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
1307 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
1308 	request->Header.PageNumber = 1;
1309 	request->Header.PageLength = mpi_reply->Header.PageLength;
1310 	request->Header.PageVersion = mpi_reply->Header.PageVersion;
1311 	request->PageAddress = htole32(form | handle);
1312 	cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
1313 	cm->cm_sge = &request->PageBufferSGE;
1314 	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
1315 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
1316 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
1317 	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
1318 	if (!page) {
1319 		printf("%s: page alloc failed\n", __func__);
1320 		error = ENOMEM;
1321 		goto out;
1322 	}
1323 	cm->cm_data = page;
1324 
1325 	error = mps_wait_command(sc, &cm, 60, CAN_SLEEP);
1326 	if (cm != NULL)
1327 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
1328 	if (error || (reply == NULL)) {
1329 		/* FIXME */
1330 		/*
1331 		 * If the request returns an error then we need to do a diag
1332 		 * reset
1333 		 */
1334 		printf("%s: request for page completed with error %d\n",
1335 		    __func__, error);
1336 		error = ENXIO;
1337 		goto out;
1338 	}
1339 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
1340 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
1341 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
1342 		/* FIXME */
1343 		/*
1344 		 * If the request returns an error then we need to do a diag
1345 		 * reset
1346 		 */
1347 		printf("%s: page read with error; iocstatus = 0x%x\n",
1348 		    __func__, ioc_status);
1349 		error = ENXIO;
1350 		goto out;
1351 	}
1352 	bcopy(page, config_page, MIN(cm->cm_length,
1353 	    sizeof(Mpi2RaidVolPage1_t)));
1354 out:
1355 	free(page, M_MPT2);
1356 	if (cm)
1357 		mps_free_command(sc, cm);
1358 	return (error);
1359 }
1360 
1361 /**
1362  * mps_config_get_volume_wwid - returns wwid given the volume handle
1363  * @sc: per adapter object
1364  * @volume_handle: volume handle
1365  * @wwid: volume wwid
1366  * Context: sleep.
1367  *
1368  * Returns 0 for success, non-zero for failure.
1369  */
1370 int
mps_config_get_volume_wwid(struct mps_softc * sc,u16 volume_handle,u64 * wwid)1371 mps_config_get_volume_wwid(struct mps_softc *sc, u16 volume_handle, u64 *wwid)
1372 {
1373 	Mpi2ConfigReply_t mpi_reply;
1374 	Mpi2RaidVolPage1_t raid_vol_pg1;
1375 
1376 	*wwid = 0;
1377 	if (!(mps_config_get_raid_volume_pg1(sc, &mpi_reply, &raid_vol_pg1,
1378 	    MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, volume_handle))) {
1379 		*wwid = le64toh((u64)raid_vol_pg1.WWID.High << 32 |
1380 		    raid_vol_pg1.WWID.Low);
1381 		return 0;
1382 	} else
1383 		return -1;
1384 }
1385 
1386 /**
1387  * mps_config_get_pd_pg0 - obtain raid phys disk page 0
1388  * @sc: per adapter object
1389  * @mpi_reply: reply mf payload returned from firmware
1390  * @config_page: contents of the config page
1391  * @page_address: form and handle value used to get page
1392  * Context: sleep.
1393  *
1394  * Returns 0 for success, non-zero for failure.
1395  */
1396 int
mps_config_get_raid_pd_pg0(struct mps_softc * sc,Mpi2ConfigReply_t * mpi_reply,Mpi2RaidPhysDiskPage0_t * config_page,u32 page_address)1397 mps_config_get_raid_pd_pg0(struct mps_softc *sc, Mpi2ConfigReply_t *mpi_reply,
1398     Mpi2RaidPhysDiskPage0_t *config_page, u32 page_address)
1399 {
1400 	MPI2_CONFIG_REQUEST *request;
1401 	MPI2_CONFIG_REPLY *reply = NULL;
1402 	struct mps_command *cm;
1403 	Mpi2RaidPhysDiskPage0_t *page = NULL;
1404 	int error = 0;
1405 	u16 ioc_status;
1406 
1407 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
1408 
1409 	if ((cm = mps_alloc_command(sc)) == NULL) {
1410 		printf("%s: command alloc failed @ line %d\n", __func__,
1411 		    __LINE__);
1412 		error = EBUSY;
1413 		goto out;
1414 	}
1415 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
1416 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
1417 	request->Function = MPI2_FUNCTION_CONFIG;
1418 	request->Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
1419 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
1420 	request->Header.PageNumber = 0;
1421 	request->Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION;
1422 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
1423 	cm->cm_data = NULL;
1424 
1425 	/*
1426 	 * This page must be polled because the IOC isn't ready yet when this
1427 	 * page is needed.
1428 	 */
1429 	error = mps_wait_command(sc, &cm, 60, 0);
1430 	if (cm != NULL)
1431 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
1432 	if (error || (reply == NULL)) {
1433 		/* FIXME */
1434 		/* If the poll returns error then we need to do diag reset */
1435 		printf("%s: poll for header completed with error %d\n",
1436 		    __func__, error);
1437 		error = ENXIO;
1438 		goto out;
1439 	}
1440 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
1441 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
1442 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
1443 		/* FIXME */
1444 		/* If the poll returns error then we need to do diag reset */
1445 		printf("%s: header read with error; iocstatus = 0x%x\n",
1446 		    __func__, ioc_status);
1447 		error = ENXIO;
1448 		goto out;
1449 	}
1450 	/* We have to do free and alloc for the reply-free and reply-post
1451 	 * counters to match - Need to review the reply FIFO handling.
1452 	 */
1453 	mps_free_command(sc, cm);
1454 
1455 	if ((cm = mps_alloc_command(sc)) == NULL) {
1456 		printf("%s: command alloc failed @ line %d\n", __func__,
1457 		    __LINE__);
1458 		error = EBUSY;
1459 		goto out;
1460 	}
1461 	request = (MPI2_CONFIG_REQUEST *)cm->cm_req;
1462 	bzero(request, sizeof(MPI2_CONFIG_REQUEST));
1463 	request->Function = MPI2_FUNCTION_CONFIG;
1464 	request->Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
1465 	request->Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
1466 	request->Header.PageNumber = 0;
1467 	request->Header.PageLength = mpi_reply->Header.PageLength;
1468 	request->Header.PageVersion = mpi_reply->Header.PageVersion;
1469 	request->PageAddress = page_address;
1470 	cm->cm_length = le16toh(mpi_reply->Header.PageLength) * 4;
1471 	cm->cm_sge = &request->PageBufferSGE;
1472 	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
1473 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
1474 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
1475 	page = malloc(cm->cm_length, M_MPT2, M_ZERO | M_NOWAIT);
1476 	if (!page) {
1477 		printf("%s: page alloc failed\n", __func__);
1478 		error = ENOMEM;
1479 		goto out;
1480 	}
1481 	cm->cm_data = page;
1482 
1483 	/*
1484 	 * This page must be polled because the IOC isn't ready yet when this
1485 	 * page is needed.
1486 	 */
1487 	error = mps_wait_command(sc, &cm, 60, 0);
1488 	if (cm != NULL)
1489 		reply = (MPI2_CONFIG_REPLY *)cm->cm_reply;
1490 	if (error || (reply == NULL)) {
1491 		/* FIXME */
1492 		/* If the poll returns error then we need to do diag reset */
1493 		printf("%s: poll for page completed with error %d\n",
1494 		    __func__, error);
1495 		error = ENXIO;
1496 		goto out;
1497 	}
1498 	ioc_status = le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK;
1499 	bcopy(reply, mpi_reply, sizeof(MPI2_CONFIG_REPLY));
1500 	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) {
1501 		/* FIXME */
1502 		/* If the poll returns error then we need to do diag reset */
1503 		printf("%s: page read with error; iocstatus = 0x%x\n",
1504 		    __func__, ioc_status);
1505 		error = ENXIO;
1506 		goto out;
1507 	}
1508 	bcopy(page, config_page, MIN(cm->cm_length,
1509 	    sizeof(Mpi2RaidPhysDiskPage0_t)));
1510 out:
1511 	free(page, M_MPT2);
1512 	if (cm)
1513 		mps_free_command(sc, cm);
1514 	return (error);
1515 }
1516