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