xref: /titanic_50/usr/src/uts/i86pc/i86hvm/io/pv_cmdk.c (revision 1787f50304cd7e85910a3e14f639ac892c0d76b7)
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 
44 extern const struct dev_ops	cmdk_ops;
45 extern void			*cmdk_state;
46 
47 /*
48  * Globals required by xdf_shell.c
49  */
50 const char		*xdfs_c_name = "cmdk";
51 const char		*xdfs_c_linkinfo = "PV Common Direct Access Disk";
52 void			**xdfs_c_hvm_ss = &cmdk_state;
53 const size_t		xdfs_c_hvm_ss_size = sizeof (struct cmdk);
54 const struct dev_ops	*xdfs_c_hvm_dev_ops = &cmdk_ops;
55 
56 const 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  */
118 static int
119 xdfs_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  */
165 static int
166 xdfs_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 
198 err:
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  */
212 static int
213 xdfs_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 
252 err:
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  */
266 static int
267 xdfs_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 
313 err:
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  */
324 static int
325 xdfs_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  */
362 static int
363 xdfs_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 
402 static int
403 xdfs_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 
453 out:
454 	kmem_free(rwcmdp, sizeof (struct dadkio_rwcmd));
455 	return (status);
456 }
457 
458 
459 /*
460  * xdf_shell callback functions
461  */
462 /*ARGSUSED*/
463 int
464 xdfs_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  */
554 void
555 xdfs_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 
582 int
583 xdfs_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 
610 boolean_t
611 xdfs_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 
651 err:
652 	kmem_free(ap, NBPSCTR);
653 	return (B_FALSE);
654 }
655 
656 char *
657 xdfs_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*/
663 int
664 xdfs_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*/
671 void
672 xdfs_c_attach(xdfs_state_t *xsp)
673 {
674 }
675