xref: /freebsd/sys/dev/aac/aac_disk.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1  /*-
2   * SPDX-License-Identifier: BSD-2-Clause
3   *
4   * Copyright (c) 2000 Michael Smith
5   * Copyright (c) 2001 Scott Long
6   * Copyright (c) 2000 BSDi
7   * Copyright (c) 2001 Adaptec, Inc.
8   * All rights reserved.
9   *
10   * Redistribution and use in source and binary forms, with or without
11   * modification, are permitted provided that the following conditions
12   * are met:
13   * 1. Redistributions of source code must retain the above copyright
14   *    notice, this list of conditions and the following disclaimer.
15   * 2. Redistributions in binary form must reproduce the above copyright
16   *    notice, this list of conditions and the following disclaimer in the
17   *    documentation and/or other materials provided with the distribution.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22   * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29   * SUCH DAMAGE.
30   */
31  
32  #include <sys/cdefs.h>
33  #include "opt_aac.h"
34  
35  #include <sys/param.h>
36  #include <sys/systm.h>
37  #include <sys/kernel.h>
38  #include <sys/module.h>
39  
40  #include <sys/bus.h>
41  #include <sys/conf.h>
42  #include <sys/disk.h>
43  
44  #include <vm/vm.h>
45  #include <vm/pmap.h>
46  
47  #include <machine/md_var.h>
48  #include <machine/bus.h>
49  #include <sys/rman.h>
50  
51  #include <dev/aac/aacreg.h>
52  #include <sys/aac_ioctl.h>
53  #include <dev/aac/aacvar.h>
54  
55  /*
56   * Interface to parent.
57   */
58  static int aac_disk_probe(device_t dev);
59  static int aac_disk_attach(device_t dev);
60  static int aac_disk_detach(device_t dev);
61  
62  /*
63   * Interface to the device switch.
64   */
65  static	disk_open_t	aac_disk_open;
66  static	disk_close_t	aac_disk_close;
67  static	disk_strategy_t	aac_disk_strategy;
68  static	dumper_t	aac_disk_dump;
69  
70  static device_method_t aac_disk_methods[] = {
71  	DEVMETHOD(device_probe,	aac_disk_probe),
72  	DEVMETHOD(device_attach,	aac_disk_attach),
73  	DEVMETHOD(device_detach,	aac_disk_detach),
74  	DEVMETHOD_END
75  };
76  
77  static driver_t aac_disk_driver = {
78  	"aacd",
79  	aac_disk_methods,
80  	sizeof(struct aac_disk)
81  };
82  
83  DRIVER_MODULE(aacd, aac, aac_disk_driver, NULL, NULL);
84  
85  /*
86   * Handle open from generic layer.
87   *
88   * This is called by the diskslice code on first open in order to get the
89   * basic device geometry parameters.
90   */
91  static int
aac_disk_open(struct disk * dp)92  aac_disk_open(struct disk *dp)
93  {
94  	struct aac_disk	*sc;
95  
96  	fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
97  
98  	sc = (struct aac_disk *)dp->d_drv1;
99  
100  	if (sc == NULL) {
101  		printf("aac_disk_open: No Softc\n");
102  		return (ENXIO);
103  	}
104  
105  	/* check that the controller is up and running */
106  	if (sc->ad_controller->aac_state & AAC_STATE_SUSPEND) {
107  		device_printf(sc->ad_controller->aac_dev,
108  		    "Controller Suspended controller state = 0x%x\n",
109  		    sc->ad_controller->aac_state);
110  		return(ENXIO);
111  	}
112  
113  	sc->ad_flags |= AAC_DISK_OPEN;
114  	return (0);
115  }
116  
117  /*
118   * Handle last close of the disk device.
119   */
120  static int
aac_disk_close(struct disk * dp)121  aac_disk_close(struct disk *dp)
122  {
123  	struct aac_disk	*sc;
124  
125  	fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
126  
127  	sc = (struct aac_disk *)dp->d_drv1;
128  
129  	if (sc == NULL)
130  		return (ENXIO);
131  
132  	sc->ad_flags &= ~AAC_DISK_OPEN;
133  	return (0);
134  }
135  
136  /*
137   * Handle an I/O request.
138   */
139  static void
aac_disk_strategy(struct bio * bp)140  aac_disk_strategy(struct bio *bp)
141  {
142  	struct aac_disk	*sc;
143  
144  	sc = (struct aac_disk *)bp->bio_disk->d_drv1;
145  	fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
146  
147  	/* bogus disk? */
148  	if (sc == NULL) {
149  		bp->bio_flags |= BIO_ERROR;
150  		bp->bio_error = EINVAL;
151  		biodone(bp);
152  		return;
153  	}
154  
155  	/* do-nothing operation? */
156  	if (bp->bio_bcount == 0) {
157  		bp->bio_resid = bp->bio_bcount;
158  		biodone(bp);
159  		return;
160  	}
161  
162  	if ((bp->bio_cmd != BIO_READ) && (bp->bio_cmd != BIO_WRITE)) {
163  		biofinish(bp, NULL, EOPNOTSUPP);
164  		return;
165  	}
166  
167  	/* perform accounting */
168  
169  	/* pass the bio to the controller - it can work out who we are */
170  	mtx_lock(&sc->ad_controller->aac_io_lock);
171  	aac_submit_bio(bp);
172  	mtx_unlock(&sc->ad_controller->aac_io_lock);
173  }
174  
175  /*
176   * Map the S/G elements for doing a dump.
177   */
178  static void
aac_dump_map_sg(void * arg,bus_dma_segment_t * segs,int nsegs,int error)179  aac_dump_map_sg(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
180  {
181  	struct aac_fib *fib;
182  	struct aac_blockwrite *bw;
183  	struct aac_sg_table *sg;
184  	int i;
185  
186  	fib = (struct aac_fib *)arg;
187  	bw = (struct aac_blockwrite *)&fib->data[0];
188  	sg = &bw->SgMap;
189  
190  	if (sg != NULL) {
191  		sg->SgCount = nsegs;
192  		for (i = 0; i < nsegs; i++) {
193  			if (segs[i].ds_addr >= BUS_SPACE_MAXADDR_32BIT)
194  				return;
195  			sg->SgEntry[i].SgAddress = segs[i].ds_addr;
196  			sg->SgEntry[i].SgByteCount = segs[i].ds_len;
197  		}
198  		fib->Header.Size = nsegs * sizeof(struct aac_sg_entry);
199  	}
200  }
201  
202  /*
203   * Map the S/G elements for doing a dump on 64-bit capable devices.
204   */
205  static void
aac_dump_map_sg64(void * arg,bus_dma_segment_t * segs,int nsegs,int error)206  aac_dump_map_sg64(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
207  {
208  	struct aac_fib *fib;
209  	struct aac_blockwrite64 *bw;
210  	struct aac_sg_table64 *sg;
211  	int i;
212  
213  	fib = (struct aac_fib *)arg;
214  	bw = (struct aac_blockwrite64 *)&fib->data[0];
215  	sg = &bw->SgMap64;
216  
217  	if (sg != NULL) {
218  		sg->SgCount = nsegs;
219  		for (i = 0; i < nsegs; i++) {
220  			sg->SgEntry64[i].SgAddress = segs[i].ds_addr;
221  			sg->SgEntry64[i].SgByteCount = segs[i].ds_len;
222  		}
223  		fib->Header.Size = nsegs * sizeof(struct aac_sg_entry64);
224  	}
225  }
226  
227  /*
228   * Dump memory out to an array
229   *
230   * Send out one command at a time with up to maxio of data.
231   */
232  static int
aac_disk_dump(void * arg,void * virtual,off_t offset,size_t length)233  aac_disk_dump(void *arg, void *virtual, off_t offset, size_t length)
234  {
235  	struct aac_disk *ad;
236  	struct aac_softc *sc;
237  	struct aac_fib *fib;
238  	size_t len, maxio;
239  	int size;
240  	static bus_dmamap_t dump_datamap;
241  	static int first = 0;
242  	struct disk *dp;
243  	bus_dmamap_callback_t *callback;
244  	u_int32_t command;
245  
246  	dp = arg;
247  	ad = dp->d_drv1;
248  
249  	if (ad == NULL)
250  		return (EINVAL);
251  
252  	sc= ad->ad_controller;
253  
254  	if (!first) {
255  		first = 1;
256  		if (bus_dmamap_create(sc->aac_buffer_dmat, 0, &dump_datamap)) {
257  			device_printf(sc->aac_dev,
258  			    "bus_dmamap_create failed\n");
259  			return (ENOMEM);
260  		}
261  	}
262  
263  	/* Skip aac_alloc_sync_fib().  We don't want to mess with sleep locks */
264  	fib = &sc->aac_common->ac_sync_fib;
265  
266  	while (length > 0) {
267  		maxio = sc->aac_max_sectors << 9;
268  		len = (length > maxio) ? maxio : length;
269  		if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
270  			struct aac_blockwrite *bw;
271  			bw = (struct aac_blockwrite *)&fib->data[0];
272  			bw->Command = VM_CtBlockWrite;
273  			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
274  			bw->BlockNumber = offset / AAC_BLOCK_SIZE;
275  			bw->ByteCount = len;
276  			bw->Stable = CUNSTABLE;
277  			command = ContainerCommand;
278  			callback = aac_dump_map_sg;
279  			size = sizeof(struct aac_blockwrite);
280  		} else {
281  			struct aac_blockwrite64 *bw;
282  			bw = (struct aac_blockwrite64 *)&fib->data[0];
283  			bw->Command = VM_CtHostWrite64;
284  			bw->ContainerId = ad->ad_container->co_mntobj.ObjectId;
285  			bw->BlockNumber = offset / AAC_BLOCK_SIZE;
286  			bw->SectorCount = len / AAC_BLOCK_SIZE;
287  			bw->Pad = 0;
288  			bw->Flags = 0;
289  			command = ContainerCommand64;
290  			callback = aac_dump_map_sg64;
291  			size = sizeof(struct aac_blockwrite64);
292  		}
293  
294  		/*
295  		 * There really isn't any way to recover from errors or
296  		 * resource shortages here.  Oh well.  Because of that, don't
297  		 * bother trying to send the command from the callback; there
298  		 * is too much required context.
299  		 */
300  		if (bus_dmamap_load(sc->aac_buffer_dmat, dump_datamap, virtual,
301  		    len, callback, fib, BUS_DMA_NOWAIT) != 0)
302  			return (ENOMEM);
303  
304  		bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap,
305  		    BUS_DMASYNC_PREWRITE);
306  
307  		/* fib->Header.Size is set in aac_dump_map_sg */
308  		size += fib->Header.Size;
309  
310  		if (aac_sync_fib(sc, command, 0, fib, size)) {
311  			device_printf(sc->aac_dev,
312  			     "Error dumping block to 0x%jx\n",
313  			     (uintmax_t)offset);
314  			return (EIO);
315  		}
316  
317  		bus_dmamap_sync(sc->aac_buffer_dmat, dump_datamap,
318  		    BUS_DMASYNC_POSTWRITE);
319  
320  		bus_dmamap_unload(sc->aac_buffer_dmat, dump_datamap);
321  
322  		length -= len;
323  		offset += len;
324  		virtual = (uint8_t *)virtual + len;
325  	}
326  
327  	return (0);
328  }
329  
330  /*
331   * Handle completion of an I/O request.
332   */
333  void
aac_biodone(struct bio * bp)334  aac_biodone(struct bio *bp)
335  {
336  	fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
337  
338  	if (bp->bio_flags & BIO_ERROR) {
339  		bp->bio_resid = bp->bio_bcount;
340  		disk_err(bp, "hard error", -1, 1);
341  	}
342  
343  	biodone(bp);
344  }
345  
346  /*
347   * Stub only.
348   */
349  static int
aac_disk_probe(device_t dev)350  aac_disk_probe(device_t dev)
351  {
352  
353  	fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
354  
355  	return (0);
356  }
357  
358  /*
359   * Attach a unit to the controller.
360   */
361  static int
aac_disk_attach(device_t dev)362  aac_disk_attach(device_t dev)
363  {
364  	struct aac_disk	*sc;
365  
366  	sc = (struct aac_disk *)device_get_softc(dev);
367  	fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
368  
369  	/* initialise our softc */
370  	sc->ad_controller =
371  	    (struct aac_softc *)device_get_softc(device_get_parent(dev));
372  	sc->ad_container = device_get_ivars(dev);
373  	sc->ad_dev = dev;
374  
375  	/*
376  	 * require that extended translation be enabled - other drivers read the
377  	 * disk!
378  	 */
379  	sc->ad_size = sc->ad_container->co_mntobj.Capacity;
380  	if (sc->ad_controller->flags & AAC_FLAGS_LBA_64BIT)
381  		sc->ad_size += (u_int64_t)
382  			sc->ad_container->co_mntobj.CapacityHigh << 32;
383  	if (sc->ad_size >= (2 * 1024 * 1024)) {		/* 2GB */
384  		sc->ad_heads = 255;
385  		sc->ad_sectors = 63;
386  	} else if (sc->ad_size >= (1 * 1024 * 1024)) {	/* 1GB */
387  		sc->ad_heads = 128;
388  		sc->ad_sectors = 32;
389  	} else {
390  		sc->ad_heads = 64;
391  		sc->ad_sectors = 32;
392  	}
393  	sc->ad_cylinders = (sc->ad_size / (sc->ad_heads * sc->ad_sectors));
394  
395  	device_printf(dev, "%juMB (%ju sectors)\n",
396  		      (intmax_t)sc->ad_size / ((1024 * 1024) / AAC_BLOCK_SIZE),
397  		      (intmax_t)sc->ad_size);
398  
399  	/* attach a generic disk device to ourselves */
400  	sc->unit = device_get_unit(dev);
401  	sc->ad_disk = disk_alloc();
402  	sc->ad_disk->d_drv1 = sc;
403  	sc->ad_disk->d_flags = DISKFLAG_UNMAPPED_BIO;
404  	sc->ad_disk->d_name = "aacd";
405  	sc->ad_disk->d_maxsize = sc->ad_controller->aac_max_sectors << 9;
406  	sc->ad_disk->d_open = aac_disk_open;
407  	sc->ad_disk->d_close = aac_disk_close;
408  	sc->ad_disk->d_strategy = aac_disk_strategy;
409  	sc->ad_disk->d_dump = aac_disk_dump;
410  	sc->ad_disk->d_sectorsize = AAC_BLOCK_SIZE;
411  	sc->ad_disk->d_mediasize = (off_t)sc->ad_size * AAC_BLOCK_SIZE;
412  	sc->ad_disk->d_fwsectors = sc->ad_sectors;
413  	sc->ad_disk->d_fwheads = sc->ad_heads;
414  	sc->ad_disk->d_unit = sc->unit;
415  	disk_create(sc->ad_disk, DISK_VERSION);
416  
417  	return (0);
418  }
419  
420  /*
421   * Disconnect ourselves from the system.
422   */
423  static int
aac_disk_detach(device_t dev)424  aac_disk_detach(device_t dev)
425  {
426  	struct aac_disk *sc;
427  
428  	sc = (struct aac_disk *)device_get_softc(dev);
429  	fwprintf(NULL, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
430  
431  	if (sc->ad_flags & AAC_DISK_OPEN)
432  		return(EBUSY);
433  
434  	disk_destroy(sc->ad_disk);
435  
436  	return(0);
437  }
438