pv_cmdk.c revision 8863:94039d51dda4
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 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <io/xdf_shell.h>
27
28/*
29 * We're emulating (and possibly layering on top of) cmdk devices, so xdf
30 * disk unit mappings must match up with cmdk disk unit mappings'.
31 */
32#if !defined(XDF_PSHIFT)
33#error "can't find definition for xdf unit mappings - XDF_PSHIFT"
34#endif /* XDF_PSHIFT */
35
36#if !defined(CMDK_UNITSHF)
37#error "can't find definition for cmdk unit mappings - CMDK_UNITSHF"
38#endif /* CMDK_UNITSHF */
39
40#if ((XDF_PSHIFT - CMDK_UNITSHF) != 0)
41#error "cmdk and xdf unit mappings don't match."
42#endif /* ((XDF_PSHIFT - CMDK_UNITSHF) != 0) */
43
44extern const struct dev_ops	cmdk_ops;
45extern void			*cmdk_state;
46
47/*
48 * Globals required by xdf_shell.c
49 */
50const char		*xdfs_c_name = "cmdk";
51const char		*xdfs_c_linkinfo = "PV Common Direct Access Disk";
52void			**xdfs_c_hvm_ss = &cmdk_state;
53const size_t		xdfs_c_hvm_ss_size = sizeof (struct cmdk);
54const struct dev_ops	*xdfs_c_hvm_dev_ops = &cmdk_ops;
55
56const xdfs_h2p_map_t xdfs_c_h2p_map[] = {
57	/*
58	 * The paths mapping here are very specific to xen and qemu.  When a
59	 * domU is booted under xen in HVM mode, qemu is normally used to
60	 * emulate up to four ide disks.  These disks always have the four
61	 * path listed below.  To configure an emulated ide device, the
62	 * xen domain configuration file normally has an entry that looks
63	 * like this:
64	 *	disk = [ 'file:/foo.img,hda,w' ]
65	 *
66	 * The part we're interested in is the 'hda', which we'll call the
67	 * xen disk device name here.  The xen management tools (which parse
68	 * the xen domain configuration file and launch qemu) makes the
69	 * following assumptions about this value:
70	 *	hda == emulated ide disk 0 (ide bus 0, master)
71	 *	hdb == emulated ide disk 1 (ide bus 0, slave)
72	 *	hdc == emulated ide disk 2 (ide bus 1, master)
73	 *	hdd == emulated ide disk 3 (ide bus 1, slave)
74	 *
75	 * (Uncoincidentally, these xen disk device names actually map to
76	 * the /dev filesystem names of ide disk devices in Linux.  So in
77	 * Linux /dev/hda is the first ide disk.)  So for the first part of
78	 * our mapping we've just hardcoded the cmdk paths that we know
79	 * qemu will use.
80	 *
81	 * To understand the second half of the mapping (ie, the xdf device
82	 * that each emulated cmdk device should be mapped two) we need to
83	 * know the solaris device node address that will be assigned to
84	 * each xdf device.  (The device node address is the decimal
85	 * number that comes after the "xdf@" in the device path.)
86	 *
87	 * So the question becomes, how do we know what the xenstore device
88	 * id for emulated disk will be?  Well, it turns out that since the
89	 * xen management tools expect the disk device names to be Linux
90	 * device names, those same management tools assign each disk a
91	 * device id that matches the dev_t of the corresponding device
92	 * under Linux.  (Big shocker.)  This xen device name-to-id mapping
93	 * is currently all hard coded here:
94	 *	xen.hg/tools/python/xen/util/blkif.py`blkdev_name_to_number()
95	 *
96	 * So looking at the code above we can see the following xen disk
97	 * device name to xenstore device id mappings:
98	 *	'hda' == 0t768  == ((3  * 256) + (0 * 64))
99	 *	'hdb' == 0t832  == ((3  * 256) + (1 * 64))
100	 *	'hdc' == 0t5632 == ((22 * 256) + (0 * 64))
101	 *	'hdd' == 0t5696 == ((22 * 256) + (1 * 64))
102	 */
103	{ "/pci@0,0/pci-ide@1,1/ide@0/cmdk@0,0", "/xpvd/xdf@768" },
104	{ "/pci@0,0/pci-ide@1,1/ide@0/cmdk@1,0", "/xpvd/xdf@832" },
105	{ "/pci@0,0/pci-ide@1,1/ide@1/cmdk@0,0", "/xpvd/xdf@5632" },
106	{ "/pci@0,0/pci-ide@1,1/ide@1/cmdk@1,0", "/xpvd/xdf@5696" },
107	{ NULL, 0 }
108};
109
110/*
111 * Private functions
112 */
113/*
114 * xdfs_get_modser() is basically a local copy of
115 * cmdk_get_modser() modified to work without the dadk layer.
116 * (which the non-pv version of the cmdk driver uses.)
117 */
118static int
119xdfs_get_modser(xdfs_state_t *xsp, int ioccmd, char *buf, int len)
120{
121	struct scsi_device	*scsi_device;
122	opaque_t		ctlobjp;
123	dadk_ioc_string_t	strarg;
124	char			*s;
125	char			ch;
126	boolean_t		ret;
127	int			i;
128	int			tb;
129
130	strarg.is_buf = buf;
131	strarg.is_size = len;
132	scsi_device = ddi_get_driver_private(xsp->xdfss_dip);
133	ctlobjp = scsi_device->sd_address.a_hba_tran;
134	if (CTL_IOCTL(ctlobjp,
135	    ioccmd, (uintptr_t)&strarg, FNATIVE | FKIOCTL) != 0)
136		return (0);
137
138	/*
139	 * valid model/serial string must contain a non-zero non-space
140	 * trim trailing spaces/NULL
141	 */
142	ret = B_FALSE;
143	s = buf;
144	for (i = 0; i < strarg.is_size; i++) {
145		ch = *s++;
146		if (ch != ' ' && ch != '\0')
147			tb = i + 1;
148		if (ch != ' ' && ch != '\0' && ch != '0')
149			ret = B_TRUE;
150	}
151
152	if (ret == B_FALSE)
153		return (0);
154
155	return (tb);
156}
157
158/*
159 * xdfs_devid_modser() is basically a copy of cmdk_devid_modser()
160 * that has been modified to use local pv cmdk driver functions.
161 *
162 * Build a devid from the model and serial number
163 * Return DDI_SUCCESS or DDI_FAILURE.
164 */
165static int
166xdfs_devid_modser(xdfs_state_t *xsp)
167{
168	int	rc = DDI_FAILURE;
169	char	*hwid;
170	int	modlen;
171	int	serlen;
172
173	/*
174	 * device ID is a concatenation of model number, '=', serial number.
175	 */
176	hwid = kmem_alloc(CMDK_HWIDLEN, KM_SLEEP);
177	modlen = xdfs_get_modser(xsp, DIOCTL_GETMODEL, hwid, CMDK_HWIDLEN);
178	if (modlen == 0)
179		goto err;
180
181	hwid[modlen++] = '=';
182	serlen = xdfs_get_modser(xsp, DIOCTL_GETSERIAL,
183	    hwid + modlen, CMDK_HWIDLEN - modlen);
184	if (serlen == 0)
185		goto err;
186
187	hwid[modlen + serlen] = 0;
188
189	/* Initialize the device ID, trailing NULL not included */
190	rc = ddi_devid_init(xsp->xdfss_dip, DEVID_ATA_SERIAL, modlen + serlen,
191	    hwid, (ddi_devid_t *)&xsp->xdfss_tgt_devid);
192	if (rc != DDI_SUCCESS)
193		goto err;
194
195	kmem_free(hwid, CMDK_HWIDLEN);
196	return (DDI_SUCCESS);
197
198err:
199	kmem_free(hwid, CMDK_HWIDLEN);
200	return (DDI_FAILURE);
201}
202
203/*
204 * xdfs_devid_read() is basically a local copy of
205 * cmdk_devid_read() modified to work without the dadk layer.
206 * (which the non-pv version of the cmdk driver uses.)
207 *
208 * Read a devid from on the first block of the last track of
209 * the last cylinder.  Make sure what we read is a valid devid.
210 * Return DDI_SUCCESS or DDI_FAILURE.
211 */
212static int
213xdfs_devid_read(xdfs_state_t *xsp)
214{
215	diskaddr_t	blk;
216	struct dk_devid *dkdevidp;
217	uint_t		*ip, chksum;
218	int		i;
219
220	if (cmlb_get_devid_block(xsp->xdfss_cmlbhandle, &blk, 0) != 0)
221		return (DDI_FAILURE);
222
223	dkdevidp = kmem_zalloc(NBPSCTR, KM_SLEEP);
224	if (xdfs_lb_rdwr(xsp->xdfss_dip,
225	    TG_READ, dkdevidp, blk, NBPSCTR, NULL) != 0)
226		goto err;
227
228	/* Validate the revision */
229	if ((dkdevidp->dkd_rev_hi != DK_DEVID_REV_MSB) ||
230	    (dkdevidp->dkd_rev_lo != DK_DEVID_REV_LSB))
231		goto err;
232
233	/* Calculate the checksum */
234	chksum = 0;
235	ip = (uint_t *)dkdevidp;
236	for (i = 0; i < ((NBPSCTR - sizeof (int))/sizeof (int)); i++)
237		chksum ^= ip[i];
238	if (DKD_GETCHKSUM(dkdevidp) != chksum)
239		goto err;
240
241	/* Validate the device id */
242	if (ddi_devid_valid((ddi_devid_t)dkdevidp->dkd_devid) != DDI_SUCCESS)
243		goto err;
244
245	/* keep a copy of the device id */
246	i = ddi_devid_sizeof((ddi_devid_t)dkdevidp->dkd_devid);
247	xsp->xdfss_tgt_devid = kmem_alloc(i, KM_SLEEP);
248	bcopy(dkdevidp->dkd_devid, xsp->xdfss_tgt_devid, i);
249	kmem_free(dkdevidp, NBPSCTR);
250	return (DDI_SUCCESS);
251
252err:
253	kmem_free(dkdevidp, NBPSCTR);
254	return (DDI_FAILURE);
255}
256
257/*
258 * xdfs_devid_fabricate() is basically a local copy of
259 * cmdk_devid_fabricate() modified to work without the dadk layer.
260 * (which the non-pv version of the cmdk driver uses.)
261 *
262 * Create a devid and write it on the first block of the last track of
263 * the last cylinder.
264 * Return DDI_SUCCESS or DDI_FAILURE.
265 */
266static int
267xdfs_devid_fabricate(xdfs_state_t *xsp)
268{
269	ddi_devid_t	devid = NULL; /* devid made by ddi_devid_init  */
270	struct dk_devid	*dkdevidp = NULL; /* devid struct stored on disk */
271	diskaddr_t	blk;
272	uint_t		*ip, chksum;
273	int		i;
274
275	if (cmlb_get_devid_block(xsp->xdfss_cmlbhandle, &blk, 0) != 0)
276		return (DDI_FAILURE);
277
278	if (ddi_devid_init(xsp->xdfss_dip, DEVID_FAB, 0, NULL, &devid) !=
279	    DDI_SUCCESS)
280		return (DDI_FAILURE);
281
282	/* allocate a buffer */
283	dkdevidp = (struct dk_devid *)kmem_zalloc(NBPSCTR, KM_SLEEP);
284
285	/* Fill in the revision */
286	dkdevidp->dkd_rev_hi = DK_DEVID_REV_MSB;
287	dkdevidp->dkd_rev_lo = DK_DEVID_REV_LSB;
288
289	/* Copy in the device id */
290	i = ddi_devid_sizeof(devid);
291	if (i > DK_DEVID_SIZE)
292		goto err;
293	bcopy(devid, dkdevidp->dkd_devid, i);
294
295	/* Calculate the chksum */
296	chksum = 0;
297	ip = (uint_t *)dkdevidp;
298	for (i = 0; i < ((NBPSCTR - sizeof (int))/sizeof (int)); i++)
299		chksum ^= ip[i];
300
301	/* Fill in the checksum */
302	DKD_FORMCHKSUM(chksum, dkdevidp);
303
304	if (xdfs_lb_rdwr(xsp->xdfss_dip,
305	    TG_WRITE, dkdevidp, blk, NBPSCTR, NULL) != 0)
306		goto err;
307
308	kmem_free(dkdevidp, NBPSCTR);
309
310	xsp->xdfss_tgt_devid = devid;
311	return (DDI_SUCCESS);
312
313err:
314	if (dkdevidp != NULL)
315		kmem_free(dkdevidp, NBPSCTR);
316	if (devid != NULL)
317		ddi_devid_free(devid);
318	return (DDI_FAILURE);
319}
320
321/*
322 * xdfs_rwcmd_copyin() is a duplicate of rwcmd_copyin().
323 */
324static int
325xdfs_rwcmd_copyin(struct dadkio_rwcmd *rwcmdp, caddr_t inaddr, int flag)
326{
327	switch (ddi_model_convert_from(flag)) {
328		case DDI_MODEL_ILP32: {
329			struct dadkio_rwcmd32 cmd32;
330
331			if (ddi_copyin(inaddr, &cmd32,
332			    sizeof (struct dadkio_rwcmd32), flag)) {
333				return (EFAULT);
334			}
335
336			rwcmdp->cmd = cmd32.cmd;
337			rwcmdp->flags = cmd32.flags;
338			rwcmdp->blkaddr = (blkaddr_t)cmd32.blkaddr;
339			rwcmdp->buflen = cmd32.buflen;
340			rwcmdp->bufaddr = (caddr_t)(intptr_t)cmd32.bufaddr;
341			/*
342			 * Note: we do not convert the 'status' field,
343			 * as it should not contain valid data at this
344			 * point.
345			 */
346			bzero(&rwcmdp->status, sizeof (rwcmdp->status));
347			break;
348		}
349		case DDI_MODEL_NONE: {
350			if (ddi_copyin(inaddr, rwcmdp,
351			    sizeof (struct dadkio_rwcmd), flag)) {
352				return (EFAULT);
353			}
354		}
355	}
356	return (0);
357}
358
359/*
360 * xdfs_rwcmd_copyout() is a duplicate of rwcmd_copyout().
361 */
362static int
363xdfs_rwcmd_copyout(struct dadkio_rwcmd *rwcmdp, caddr_t outaddr, int flag)
364{
365	switch (ddi_model_convert_from(flag)) {
366		case DDI_MODEL_ILP32: {
367			struct dadkio_rwcmd32 cmd32;
368
369			cmd32.cmd = rwcmdp->cmd;
370			cmd32.flags = rwcmdp->flags;
371			cmd32.blkaddr = rwcmdp->blkaddr;
372			cmd32.buflen = rwcmdp->buflen;
373			ASSERT64(((uintptr_t)rwcmdp->bufaddr >> 32) == 0);
374			cmd32.bufaddr = (caddr32_t)(uintptr_t)rwcmdp->bufaddr;
375
376			cmd32.status.status = rwcmdp->status.status;
377			cmd32.status.resid = rwcmdp->status.resid;
378			cmd32.status.failed_blk_is_valid =
379			    rwcmdp->status.failed_blk_is_valid;
380			cmd32.status.failed_blk = rwcmdp->status.failed_blk;
381			cmd32.status.fru_code_is_valid =
382			    rwcmdp->status.fru_code_is_valid;
383			cmd32.status.fru_code = rwcmdp->status.fru_code;
384
385			bcopy(rwcmdp->status.add_error_info,
386			    cmd32.status.add_error_info, DADKIO_ERROR_INFO_LEN);
387
388			if (ddi_copyout(&cmd32, outaddr,
389			    sizeof (struct dadkio_rwcmd32), flag))
390				return (EFAULT);
391			break;
392		}
393		case DDI_MODEL_NONE: {
394			if (ddi_copyout(rwcmdp, outaddr,
395			    sizeof (struct dadkio_rwcmd), flag))
396			return (EFAULT);
397		}
398	}
399	return (0);
400}
401
402static int
403xdfs_dioctl_rwcmd(dev_t dev, intptr_t arg, int flag)
404{
405	struct dadkio_rwcmd	*rwcmdp;
406	struct iovec		aiov;
407	struct uio		auio;
408	struct buf		*bp;
409	int			rw, status;
410
411	rwcmdp = kmem_alloc(sizeof (struct dadkio_rwcmd), KM_SLEEP);
412	status = xdfs_rwcmd_copyin(rwcmdp, (caddr_t)arg, flag);
413
414	if (status != 0)
415		goto out;
416
417	switch (rwcmdp->cmd) {
418		case DADKIO_RWCMD_READ:
419		case DADKIO_RWCMD_WRITE:
420			break;
421		default:
422			status = EINVAL;
423			goto out;
424	}
425
426	bzero((caddr_t)&aiov, sizeof (struct iovec));
427	aiov.iov_base = rwcmdp->bufaddr;
428	aiov.iov_len = rwcmdp->buflen;
429
430	bzero((caddr_t)&auio, sizeof (struct uio));
431	auio.uio_iov = &aiov;
432	auio.uio_iovcnt = 1;
433	auio.uio_loffset = (offset_t)rwcmdp->blkaddr * (offset_t)XB_BSIZE;
434	auio.uio_resid = rwcmdp->buflen;
435	auio.uio_segflg = (flag & FKIOCTL) ? UIO_SYSSPACE : UIO_USERSPACE;
436
437	/*
438	 * Tell the xdf driver that this I/O request is using an absolute
439	 * offset.
440	 */
441	bp = getrbuf(KM_SLEEP);
442	bp->b_private = (void *)XB_SLICE_NONE;
443
444	rw = ((rwcmdp->cmd == DADKIO_RWCMD_WRITE) ? B_WRITE : B_READ);
445	status = physio(xdfs_strategy, bp, dev, rw, xdfs_minphys, &auio);
446
447	biofini(bp);
448	kmem_free(bp, sizeof (buf_t));
449
450	if (status == 0)
451		status = xdfs_rwcmd_copyout(rwcmdp, (caddr_t)arg, flag);
452
453out:
454	kmem_free(rwcmdp, sizeof (struct dadkio_rwcmd));
455	return (status);
456}
457
458
459/*
460 * xdf_shell callback functions
461 */
462/*ARGSUSED*/
463int
464xdfs_c_ioctl(xdfs_state_t *xsp, dev_t dev, int part,
465    int cmd, intptr_t arg, int flag, cred_t *credp, int *rvalp, boolean_t *done)
466{
467	*done = B_TRUE;
468	switch (cmd) {
469	default:
470		*done = B_FALSE;
471		return (0);
472	case DKIOCLOCK:
473	case DKIOCUNLOCK:
474	case FDEJECT:
475	case DKIOCEJECT:
476	case CDROMEJECT: {
477		/* we don't support ejectable devices */
478		return (ENOTTY);
479	}
480	case DKIOCGETWCE:
481	case DKIOCSETWCE: {
482		/* we don't support write cache get/set */
483		return (EIO);
484	}
485	case DKIOCADDBAD: {
486		/*
487		 * This is for ata/ide bad block handling.  It is supposed
488		 * to cause the driver to re-read the bad block list and
489		 * alternate map after it has been updated.  Our driver
490		 * will refuse to attach to any disk which has a bad blocks
491		 * list defined, so there really isn't much to do here.
492		 */
493		return (0);
494	}
495	case DKIOCGETDEF: {
496		/*
497		 * I can't actually find any code that utilizes this ioctl,
498		 * hence we're leaving it explicitly unimplemented.
499		 */
500		ASSERT("ioctl cmd unsupported by xdf shell: DKIOCGETDEF");
501		return (EIO);
502	}
503	case DIOCTL_RWCMD: {
504		/*
505		 * This just seems to just be an alternate interface for
506		 * reading and writing the disk.  Great, another way to
507		 * do the same thing...
508		 */
509		return (xdfs_dioctl_rwcmd(dev, arg, flag));
510	}
511	case DKIOCINFO: {
512		int		instance = ddi_get_instance(xsp->xdfss_dip);
513		dev_info_t	*dip = xsp->xdfss_dip;
514		struct dk_cinfo	info;
515		int		rv;
516
517		/* Pass on the ioctl request, save the response */
518		if ((rv = ldi_ioctl(xsp->xdfss_tgt_lh[part],
519		    cmd, (intptr_t)&info, FKIOCTL, credp, rvalp)) != 0)
520			return (rv);
521
522		/* Update controller info */
523		info.dki_cnum = ddi_get_instance(ddi_get_parent(dip));
524		(void) strlcpy(info.dki_cname,
525		    ddi_get_name(ddi_get_parent(dip)), sizeof (info.dki_cname));
526
527		/* Update unit info. */
528		if (info.dki_ctype == DKC_VBD)
529			info.dki_ctype = DKC_DIRECT;
530		info.dki_unit = instance;
531		(void) strlcpy(info.dki_dname,
532		    ddi_driver_name(dip), sizeof (info.dki_dname));
533		info.dki_addr = 1;
534
535		if (ddi_copyout(&info, (void *)arg, sizeof (info), flag))
536			return (EFAULT);
537		return (0);
538	}
539	} /* switch (cmd) */
540	/*NOTREACHED*/
541}
542
543/*
544 * xdfs_c_devid_setup() is a slightly modified copy of cmdk_devid_setup().
545 *
546 * Create and register the devid.
547 * There are 4 different ways we can get a device id:
548 *    1. Already have one - nothing to do
549 *    2. Build one from the drive's model and serial numbers
550 *    3. Read one from the disk (first sector of last track)
551 *    4. Fabricate one and write it on the disk.
552 * If any of these succeeds, register the deviceid
553 */
554void
555xdfs_c_devid_setup(xdfs_state_t *xsp)
556{
557	int	rc;
558
559	/* Try options until one succeeds, or all have failed */
560
561	/* 1. All done if already registered */
562
563	if (xsp->xdfss_tgt_devid != NULL)
564		return;
565
566	/* 2. Build a devid from the model and serial number */
567	rc = xdfs_devid_modser(xsp);
568	if (rc != DDI_SUCCESS) {
569		/* 3. Read devid from the disk, if present */
570		rc = xdfs_devid_read(xsp);
571
572		/* 4. otherwise make one up and write it on the disk */
573		if (rc != DDI_SUCCESS)
574			rc = xdfs_devid_fabricate(xsp);
575	}
576
577	/* If we managed to get a devid any of the above ways, register it */
578	if (rc == DDI_SUCCESS)
579		(void) ddi_devid_register(xsp->xdfss_dip, xsp->xdfss_tgt_devid);
580}
581
582int
583xdfs_c_getpgeom(dev_info_t *dip, cmlb_geom_t *pgeom)
584{
585	struct scsi_device	*scsi_device;
586	struct tgdk_geom	tgdk_geom;
587	opaque_t		ctlobjp;
588	int			err;
589
590	scsi_device = ddi_get_driver_private(dip);
591	ctlobjp = scsi_device->sd_address.a_hba_tran;
592	if ((err = CTL_IOCTL(ctlobjp,
593	    DIOCTL_GETPHYGEOM, (uintptr_t)&tgdk_geom, FKIOCTL)) != 0)
594		return (err);
595
596	/* This driver won't work if this isn't true */
597	ASSERT(tgdk_geom.g_secsiz == XB_BSIZE);
598
599	pgeom->g_ncyl = tgdk_geom.g_cyl;
600	pgeom->g_acyl = tgdk_geom.g_acyl;
601	pgeom->g_nhead = tgdk_geom.g_head;
602	pgeom->g_nsect = tgdk_geom.g_sec;
603	pgeom->g_secsize = tgdk_geom.g_secsiz;
604	pgeom->g_capacity = tgdk_geom.g_cap;
605	pgeom->g_intrlv = 1;
606	pgeom->g_rpm = 3600;
607	return (0);
608}
609
610boolean_t
611xdfs_c_bb_check(xdfs_state_t *xsp)
612{
613	struct alts_parttbl	*ap;
614	diskaddr_t		nblocks, blk;
615	uint32_t		altused, altbase, altlast;
616	uint16_t		vtoctag;
617	int			alts;
618
619	/* find slice with V_ALTSCTR tag */
620	for (alts = 0; alts < NDKMAP; alts++) {
621
622		if (cmlb_partinfo(xsp->xdfss_cmlbhandle, alts,
623		    &nblocks, &blk, NULL, &vtoctag, 0) != 0) {
624			/* no partition table exists */
625			return (B_FALSE);
626		}
627
628		if ((vtoctag == V_ALTSCTR) && (nblocks > 1))
629			break;
630	}
631	if (alts >= NDKMAP)
632		return (B_FALSE); /* no V_ALTSCTR slice defined */
633
634	/* read in ALTS label block */
635	ap = (struct alts_parttbl *)kmem_zalloc(NBPSCTR, KM_SLEEP);
636	if (xdfs_lb_rdwr(xsp->xdfss_dip, TG_READ, ap, blk, NBPSCTR, NULL) != 0)
637		goto err;
638
639	altused = ap->alts_ent_used;	/* number of BB entries */
640	altbase = ap->alts_ent_base;	/* blk offset from begin slice */
641	altlast = ap->alts_ent_end;	/* blk offset to last block */
642
643	if ((altused == 0) || (altbase < 1) ||
644	    (altbase > altlast) || (altlast >= nblocks))
645		goto err;
646
647	/* we found bad block mappins */
648	kmem_free(ap, NBPSCTR);
649	return (B_TRUE);
650
651err:
652	kmem_free(ap, NBPSCTR);
653	return (B_FALSE);
654}
655
656char *
657xdfs_c_cmlb_node_type(xdfs_state_t *xsp)
658{
659	return (xsp->xdfss_tgt_is_cd ? DDI_NT_CD : DDI_NT_BLOCK);
660}
661
662/*ARGSUSED*/
663int
664xdfs_c_cmlb_alter_behavior(xdfs_state_t *xsp)
665{
666	return (xsp->xdfss_tgt_is_cd ?
667	    0 : CMLB_CREATE_ALTSLICE_VTOC_16_DTYPE_DIRECT);
668}
669
670/*ARGSUSED*/
671void
672xdfs_c_attach(xdfs_state_t *xsp)
673{
674}
675