xref: /illumos-gate/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas_smhba.c (revision 72a2c48434886fc259a17942b7fe9e13ba064071)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25  * Copyright 2023 Racktop Systems, Inc.
26  */
27 /*
28  * This file contains SM-HBA support for MPT SAS driver
29  */
30 
31 #if defined(lint) || defined(DEBUG)
32 #define	MPTSAS_DEBUG
33 #endif
34 
35 /*
36  * standard header files
37  */
38 #include <sys/note.h>
39 #include <sys/scsi/scsi.h>
40 #include <sys/pci.h>
41 #include <sys/scsi/generic/sas.h>
42 #include <sys/scsi/impl/scsi_sas.h>
43 
44 #pragma pack(1)
45 #include <sys/scsi/adapters/mpi/mpi2_type.h>
46 #include <sys/scsi/adapters/mpi/mpi2.h>
47 #include <sys/scsi/adapters/mpi/mpi2_cnfg.h>
48 #include <sys/scsi/adapters/mpi/mpi2_init.h>
49 #include <sys/scsi/adapters/mpi/mpi2_ioc.h>
50 #include <sys/scsi/adapters/mpi/mpi2_sas.h>
51 #pragma pack()
52 
53 /*
54  * private header files.
55  */
56 #include <sys/scsi/adapters/mpt_sas/mptsas_var.h>
57 #include <sys/scsi/adapters/mpt_sas/mptsas_smhba.h>
58 
59 /*
60  * SM - HBA statics
61  */
62 extern char *mptsas_driver_rev;
63 
64 static void mptsas_smhba_create_phy_props(nvlist_t **, smhba_info_t *, uint8_t,
65     uint16_t *);
66 static void mptsas_smhba_update_phy_props(mptsas_t *, dev_info_t *, nvlist_t **,
67     uint8_t);
68 
69 static void
70 mptsas_smhba_add_hba_prop(mptsas_t *mpt, data_type_t dt,
71     char *prop_name, void *prop_val);
72 
73 void
74 mptsas_smhba_show_phy_info(mptsas_t *mpt);
75 
76 static void
mptsas_smhba_add_hba_prop(mptsas_t * mpt,data_type_t dt,char * prop_name,void * prop_val)77 mptsas_smhba_add_hba_prop(mptsas_t *mpt, data_type_t dt,
78     char *prop_name, void *prop_val)
79 {
80 	ASSERT(mpt != NULL);
81 
82 	switch (dt) {
83 	case DATA_TYPE_INT32:
84 		if (ddi_prop_update_int(DDI_DEV_T_NONE, mpt->m_dip,
85 		    prop_name, *(int *)prop_val)) {
86 			mptsas_log(mpt, CE_WARN,
87 			    "%s: %s prop update failed", __func__, prop_name);
88 		}
89 		break;
90 	case DATA_TYPE_STRING:
91 		if (ddi_prop_update_string(DDI_DEV_T_NONE, mpt->m_dip,
92 		    prop_name, (char *)prop_val)) {
93 			mptsas_log(mpt, CE_WARN,
94 			    "%s: %s prop update failed", __func__, prop_name);
95 		}
96 		break;
97 	default:
98 		mptsas_log(mpt, CE_WARN, "%s: "
99 		    "Unhandled datatype(%d) for (%s). Skipping prop update.",
100 		    __func__, dt, prop_name);
101 	}
102 }
103 
104 void
mptsas_smhba_show_phy_info(mptsas_t * mpt)105 mptsas_smhba_show_phy_info(mptsas_t *mpt)
106 {
107 	int i;
108 
109 	ASSERT(mpt != NULL);
110 
111 	for (i = 0; i < MPTSAS_MAX_PHYS; i++) {
112 		mptsas_log(mpt, CE_WARN,
113 		    "phy %d, Owner hdl:0x%x, attached hdl: 0x%x,"
114 		    "attached phy identifier %d,Program link rate 0x%x,"
115 		    "hw link rate 0x%x, negotiator link rate 0x%x, path %s",
116 		    i, mpt->m_phy_info[i].smhba_info.owner_devhdl,
117 		    mpt->m_phy_info[i].smhba_info.attached_devhdl,
118 		    mpt->m_phy_info[i].smhba_info.attached_phy_identify,
119 		    mpt->m_phy_info[i].smhba_info.programmed_link_rate,
120 		    mpt->m_phy_info[i].smhba_info.hw_link_rate,
121 		    mpt->m_phy_info[i].smhba_info.negotiated_link_rate,
122 		    mpt->m_phy_info[i].smhba_info.path);
123 	}
124 }
125 
126 static void
mptsas_smhba_create_phy_props(nvlist_t ** phy_props,smhba_info_t * pSmhba,uint8_t phy_id,uint16_t * attached_devhdl)127 mptsas_smhba_create_phy_props(nvlist_t **phy_props, smhba_info_t *pSmhba,
128     uint8_t phy_id, uint16_t *attached_devhdl)
129 {
130 	(void) nvlist_alloc(phy_props, NV_UNIQUE_NAME, KM_SLEEP);
131 	(void) nvlist_add_uint8(*phy_props, SAS_PHY_ID, phy_id);
132 	(void) nvlist_add_uint8(*phy_props, "phyState",
133 	    (pSmhba->negotiated_link_rate & 0x0f));
134 	(void) nvlist_add_int8(*phy_props, SAS_NEG_LINK_RATE,
135 	    (pSmhba->negotiated_link_rate & 0x0f));
136 	(void) nvlist_add_int8(*phy_props, SAS_PROG_MIN_LINK_RATE,
137 	    (pSmhba->programmed_link_rate & 0x0f));
138 	(void) nvlist_add_int8(*phy_props, SAS_HW_MIN_LINK_RATE,
139 	    (pSmhba->hw_link_rate & 0x0f));
140 	(void) nvlist_add_int8(*phy_props, SAS_PROG_MAX_LINK_RATE,
141 	    ((pSmhba->programmed_link_rate & 0xf0) >> 4));
142 	(void) nvlist_add_int8(*phy_props, SAS_HW_MAX_LINK_RATE,
143 	    ((pSmhba->hw_link_rate & 0xf0) >> 4));
144 
145 	if (pSmhba->attached_devhdl && (attached_devhdl != NULL))
146 		*attached_devhdl = pSmhba->attached_devhdl;
147 }
148 
149 static void
mptsas_smhba_update_phy_props(mptsas_t * mpt,dev_info_t * dip,nvlist_t ** phy_props,uint8_t phy_nums)150 mptsas_smhba_update_phy_props(mptsas_t *mpt, dev_info_t *dip,
151     nvlist_t **phy_props, uint8_t phy_nums)
152 {
153 	int		rval;
154 	size_t		packed_size;
155 	char		*packed_data = NULL;
156 	nvlist_t	*nvl;
157 
158 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP) != 0) {
159 		mptsas_log(mpt, CE_WARN, "%s: nvlist_alloc() failed", __func__);
160 		return;
161 	}
162 
163 	rval = nvlist_add_nvlist_array(nvl, SAS_PHY_INFO_NVL, phy_props,
164 	    phy_nums);
165 	if (rval) {
166 		mptsas_log(mpt, CE_WARN,
167 		    " nv list array add failed, return value %d.", rval);
168 		goto exit;
169 	}
170 	(void) nvlist_size(nvl, &packed_size, NV_ENCODE_NATIVE);
171 	packed_data = kmem_zalloc(packed_size, KM_SLEEP);
172 	(void) nvlist_pack(nvl, &packed_data, &packed_size,
173 	    NV_ENCODE_NATIVE, 0);
174 
175 	(void) ddi_prop_update_byte_array(DDI_DEV_T_NONE, dip,
176 	    SAS_PHY_INFO, (uchar_t *)packed_data, packed_size);
177 
178 exit:
179 	nvlist_free(nvl);
180 
181 	if (packed_data != NULL) {
182 		kmem_free(packed_data, packed_size);
183 	}
184 }
185 
186 void
mptsas_smhba_set_one_phy_props(mptsas_t * mpt,dev_info_t * dip,uint8_t phy_id,uint16_t * attached_devhdl)187 mptsas_smhba_set_one_phy_props(mptsas_t *mpt, dev_info_t *dip, uint8_t phy_id,
188     uint16_t *attached_devhdl)
189 {
190 	nvlist_t	*phy_props;
191 
192 	ASSERT(phy_id < mpt->m_num_phys);
193 
194 	mptsas_smhba_create_phy_props(&phy_props,
195 	    &mpt->m_phy_info[phy_id].smhba_info, phy_id, attached_devhdl);
196 
197 	mptsas_smhba_update_phy_props(mpt, dip, &phy_props, 1);
198 
199 	nvlist_free(phy_props);
200 }
201 
202 void
mptsas_smhba_set_all_phy_props(mptsas_t * mpt,dev_info_t * dip,uint8_t phy_nums,mptsas_phymask_t phy_mask,uint16_t * attached_devhdl)203 mptsas_smhba_set_all_phy_props(mptsas_t *mpt, dev_info_t *dip, uint8_t phy_nums,
204     mptsas_phymask_t phy_mask, uint16_t *attached_devhdl)
205 {
206 	int		i, j;
207 	nvlist_t	**phy_props;
208 
209 	if (phy_nums == 0)
210 		return;
211 
212 	phy_props = kmem_zalloc(sizeof (nvlist_t *) * phy_nums, KM_SLEEP);
213 
214 	for (i = 0, j = 0; i < mpt->m_num_phys && j < phy_nums; i++)
215 		if (phy_mask == mpt->m_phy_info[i].phy_mask)
216 			mptsas_smhba_create_phy_props(&phy_props[j++],
217 			    &mpt->m_phy_info[i].smhba_info, i, attached_devhdl);
218 
219 	mptsas_smhba_update_phy_props(mpt, dip, phy_props, j);
220 
221 	for (i = 0; i < j && phy_props[i] != NULL; i++)
222 		nvlist_free(phy_props[i]);
223 
224 	kmem_free(phy_props, sizeof (nvlist_t *) * phy_nums);
225 }
226 
227 /*
228  * Called with PHY lock held on phyp
229  */
230 void
mptsas_smhba_log_sysevent(mptsas_t * mpt,char * subclass,char * etype,smhba_info_t * phyp)231 mptsas_smhba_log_sysevent(mptsas_t *mpt, char *subclass, char *etype,
232     smhba_info_t *phyp)
233 {
234 	nvlist_t	*attr_list;
235 	char		*pname;
236 	char		sas_addr[MPTSAS_WWN_STRLEN];
237 	uint8_t		phynum = 0;
238 	uint8_t		lrate = 0;
239 
240 	if (mpt->m_dip == NULL)
241 		return;
242 	if (phyp == NULL)
243 		return;
244 
245 	pname = kmem_zalloc(MAXPATHLEN, KM_NOSLEEP);
246 	if (pname == NULL)
247 		return;
248 
249 	if ((strcmp(subclass, ESC_SAS_PHY_EVENT) == 0) ||
250 	    (strcmp(subclass, ESC_SAS_HBA_PORT_BROADCAST) == 0)) {
251 		ASSERT(phyp != NULL);
252 		(void) strncpy(pname, phyp->path, strlen(phyp->path));
253 		phynum = phyp->phy_id;
254 		bzero(sas_addr, sizeof (sas_addr));
255 		(void) sprintf(sas_addr, "w%016"PRIx64, phyp->sas_addr);
256 		if (strcmp(etype, SAS_PHY_ONLINE) == 0) {
257 			lrate = phyp->negotiated_link_rate;
258 		}
259 	}
260 	if (strcmp(subclass, ESC_SAS_HBA_PORT_BROADCAST) == 0) {
261 		(void) ddi_pathname(mpt->m_dip, pname);
262 	}
263 
264 	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, 0) != 0) {
265 		mptsas_log(mpt, CE_WARN,
266 		    "%s: Failed to post sysevent", __func__);
267 		kmem_free(pname, MAXPATHLEN);
268 		return;
269 	}
270 
271 	if (nvlist_add_int32(attr_list, SAS_DRV_INST,
272 	    ddi_get_instance(mpt->m_dip)) != 0)
273 		goto fail;
274 
275 	if (nvlist_add_string(attr_list, SAS_PORT_ADDR, sas_addr) != 0)
276 		goto fail;
277 
278 	if (nvlist_add_string(attr_list, SAS_DEVFS_PATH, pname) != 0)
279 		goto fail;
280 
281 	if (nvlist_add_uint8(attr_list, SAS_PHY_ID, phynum) != 0)
282 		goto fail;
283 
284 	if (strcmp(etype, SAS_PHY_ONLINE) == 0) {
285 		if (nvlist_add_uint8(attr_list, SAS_LINK_RATE, lrate) != 0)
286 			goto fail;
287 	}
288 
289 	if (nvlist_add_string(attr_list, SAS_EVENT_TYPE, etype) != 0)
290 		goto fail;
291 
292 	(void) ddi_log_sysevent(mpt->m_dip, DDI_VENDOR_SUNW, EC_HBA, subclass,
293 	    attr_list, NULL, DDI_NOSLEEP);
294 
295 fail:
296 	kmem_free(pname, MAXPATHLEN);
297 	nvlist_free(attr_list);
298 }
299 
300 void
mptsas_create_phy_stats(mptsas_t * mpt,char * iport,dev_info_t * dip)301 mptsas_create_phy_stats(mptsas_t *mpt, char *iport, dev_info_t *dip)
302 {
303 	sas_phy_stats_t		*ps;
304 	smhba_info_t		*phyp;
305 	int			ndata;
306 	char			ks_name[KSTAT_STRLEN];
307 	char			phymask[MPTSAS_MAX_PHYS];
308 	int			i;
309 
310 	ASSERT(iport != NULL);
311 	ASSERT(mpt != NULL);
312 
313 	for (i = 0; i < mpt->m_num_phys; i++) {
314 
315 		bzero(phymask, sizeof (phymask));
316 		(void) sprintf(phymask, "%x", mpt->m_phy_info[i].phy_mask);
317 		if (strcmp(phymask, iport) == 0) {
318 
319 			phyp = &mpt->m_phy_info[i].smhba_info;
320 			mutex_enter(&phyp->phy_mutex);
321 
322 			if (phyp->phy_stats != NULL) {
323 				mutex_exit(&phyp->phy_mutex);
324 				/* We've already created this kstat instance */
325 				continue;
326 			}
327 
328 			ndata = (sizeof (sas_phy_stats_t)/
329 			    sizeof (kstat_named_t));
330 			(void) snprintf(ks_name, sizeof (ks_name),
331 			    "%s.%llx.%d.%d", ddi_driver_name(dip),
332 			    (longlong_t)mpt->un.m_base_wwid,
333 			    ddi_get_instance(dip), i);
334 
335 			phyp->phy_stats = kstat_create("mptsas",
336 			    ddi_get_instance(dip), ks_name, KSTAT_SAS_PHY_CLASS,
337 			    KSTAT_TYPE_NAMED, ndata, 0);
338 
339 			if (phyp->phy_stats == NULL) {
340 				mutex_exit(&phyp->phy_mutex);
341 				mptsas_log(mpt, CE_WARN,
342 				    "%s: Failed to create %s kstats", __func__,
343 				    ks_name);
344 				continue;
345 			}
346 
347 			ps = (sas_phy_stats_t *)phyp->phy_stats->ks_data;
348 
349 			kstat_named_init(&ps->seconds_since_last_reset,
350 			    "SecondsSinceLastReset", KSTAT_DATA_ULONGLONG);
351 			kstat_named_init(&ps->tx_frames,
352 			    "TxFrames", KSTAT_DATA_ULONGLONG);
353 			kstat_named_init(&ps->rx_frames,
354 			    "RxFrames", KSTAT_DATA_ULONGLONG);
355 			kstat_named_init(&ps->tx_words,
356 			    "TxWords", KSTAT_DATA_ULONGLONG);
357 			kstat_named_init(&ps->rx_words,
358 			    "RxWords", KSTAT_DATA_ULONGLONG);
359 			kstat_named_init(&ps->invalid_dword_count,
360 			    "InvalidDwordCount", KSTAT_DATA_ULONGLONG);
361 			kstat_named_init(&ps->running_disparity_error_count,
362 			    "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG);
363 			kstat_named_init(&ps->loss_of_dword_sync_count,
364 			    "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG);
365 			kstat_named_init(&ps->phy_reset_problem_count,
366 			    "PhyResetProblemCount", KSTAT_DATA_ULONGLONG);
367 
368 			phyp->phy_stats->ks_private = phyp;
369 			phyp->phy_stats->ks_update = mptsas_update_phy_stats;
370 			kstat_install(phyp->phy_stats);
371 			mutex_exit(&phyp->phy_mutex);
372 		}
373 	}
374 }
375 
376 int
mptsas_update_phy_stats(kstat_t * ks,int rw)377 mptsas_update_phy_stats(kstat_t *ks, int rw)
378 {
379 	int			ret = DDI_FAILURE;
380 	smhba_info_t		*pptr = NULL;
381 	sas_phy_stats_t		*ps = ks->ks_data;
382 	uint32_t		page_address;
383 	mptsas_t		*mpt;
384 
385 	_NOTE(ARGUNUSED(rw));
386 
387 	pptr = (smhba_info_t *)ks->ks_private;
388 	ASSERT((pptr != NULL));
389 	mpt = (mptsas_t *)pptr->mpt;
390 	ASSERT((mpt != NULL));
391 	page_address = (MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | pptr->phy_id);
392 
393 	/*
394 	 * We just want to lock against other invocations of kstat;
395 	 * we don't need to pmcs_lock_phy() for this.
396 	 */
397 	mutex_enter(&mpt->m_mutex);
398 
399 	ret = mptsas_get_sas_phy_page1(pptr->mpt, page_address, pptr);
400 
401 	if (ret == DDI_FAILURE)
402 		goto fail;
403 
404 	ps->invalid_dword_count.value.ull =
405 	    (unsigned long long)pptr->invalid_dword_count;
406 
407 	ps->running_disparity_error_count.value.ull =
408 	    (unsigned long long)pptr->running_disparity_error_count;
409 
410 	ps->loss_of_dword_sync_count.value.ull =
411 	    (unsigned long long)pptr->loss_of_dword_sync_count;
412 
413 	ps->phy_reset_problem_count.value.ull =
414 	    (unsigned long long)pptr->phy_reset_problem_count;
415 
416 	ret = DDI_SUCCESS;
417 fail:
418 	mutex_exit(&mpt->m_mutex);
419 
420 	return (ret);
421 }
422 
423 void
mptsas_destroy_phy_stats(mptsas_t * mpt)424 mptsas_destroy_phy_stats(mptsas_t *mpt)
425 {
426 	smhba_info_t	*phyp;
427 	int			i = 0;
428 
429 	ASSERT(mpt != NULL);
430 
431 	for (i = 0; i < mpt->m_num_phys; i++) {
432 		phyp = &mpt->m_phy_info[i].smhba_info;
433 		if (phyp == NULL) {
434 			continue;
435 		}
436 
437 		mutex_enter(&phyp->phy_mutex);
438 		if (phyp->phy_stats != NULL) {
439 			kstat_delete(phyp->phy_stats);
440 			phyp->phy_stats = NULL;
441 		}
442 		mutex_exit(&phyp->phy_mutex);
443 	}
444 }
445 
446 int
mptsas_smhba_phy_init(mptsas_t * mpt)447 mptsas_smhba_phy_init(mptsas_t *mpt)
448 {
449 	int		i = 0;
450 	int		rval = DDI_SUCCESS;
451 	uint32_t	page_address;
452 
453 	ASSERT(mutex_owned(&mpt->m_mutex));
454 
455 	for (i = 0; i < mpt->m_num_phys; i++) {
456 		page_address =
457 		    (MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER |
458 		    (MPI2_SAS_PHY_PGAD_PHY_NUMBER_MASK & i));
459 		rval = mptsas_get_sas_phy_page0(mpt,
460 		    page_address, &mpt->m_phy_info[i].smhba_info);
461 		if (rval != DDI_SUCCESS) {
462 			mptsas_log(mpt, CE_WARN,
463 			    "Failed to get sas phy page 0"
464 			    " for each phy");
465 			return (DDI_FAILURE);
466 		}
467 		mpt->m_phy_info[i].smhba_info.phy_id = (uint8_t)i;
468 		mpt->m_phy_info[i].smhba_info.sas_addr =
469 		    mpt->un.m_base_wwid + i;
470 		mpt->m_phy_info[i].smhba_info.mpt = mpt;
471 	}
472 	return (DDI_SUCCESS);
473 }
474 
475 int
mptsas_smhba_setup(mptsas_t * mpt)476 mptsas_smhba_setup(mptsas_t *mpt)
477 {
478 	int		sm_hba = 1;
479 	char		chiprev, hw_rev[24];
480 	char		serial_number[72];
481 	int		protocol = 0;
482 
483 	mutex_enter(&mpt->m_mutex);
484 	if (mptsas_smhba_phy_init(mpt)) {
485 		mutex_exit(&mpt->m_mutex);
486 		return (DDI_FAILURE);
487 	}
488 	mutex_exit(&mpt->m_mutex);
489 
490 	/* SM-HBA support */
491 	mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_INT32, MPTSAS_SMHBA_SUPPORTED,
492 	    &sm_hba);
493 
494 	/* SM-HBA driver version */
495 	mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_DRV_VERSION,
496 	    mptsas_driver_rev);
497 
498 	/* SM-HBA hardware version */
499 	chiprev = 'A' + mpt->m_revid;
500 	(void) snprintf(hw_rev, 2, "%s", &chiprev);
501 	mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_HWARE_VERSION,
502 	    hw_rev);
503 
504 	/* SM-HBA phy number per HBA */
505 	mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_INT32, MPTSAS_NUM_PHYS_HBA,
506 	    &(mpt->m_num_phys));
507 
508 	/* SM-HBA protocal support */
509 	protocol = SAS_SSP_SUPPORT | SAS_SATA_SUPPORT | SAS_SMP_SUPPORT;
510 	mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_INT32,
511 	    MPTSAS_SUPPORTED_PROTOCOL, &protocol);
512 
513 	mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_MANUFACTURER,
514 	    mpt->m_MANU_page0.ChipName);
515 
516 	mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_MODEL_NAME,
517 	    mpt->m_MANU_page0.BoardName);
518 
519 	/*
520 	 * VPD data is not available, we make a serial number for this.
521 	 */
522 
523 	(void) sprintf(serial_number, "%s%s%s%s%s",
524 	    mpt->m_MANU_page0.ChipName,
525 	    mpt->m_MANU_page0.ChipRevision,
526 	    mpt->m_MANU_page0.BoardName,
527 	    mpt->m_MANU_page0.BoardAssembly,
528 	    mpt->m_MANU_page0.BoardTracerNumber);
529 
530 	mptsas_smhba_add_hba_prop(mpt, DATA_TYPE_STRING, MPTSAS_SERIAL_NUMBER,
531 	    &serial_number[0]);
532 
533 	return (DDI_SUCCESS);
534 }
535