Home | History | Annotate | Line # | Download | only in dev
      1 /* $NetBSD: ld_thunkbus.c,v 1.34 2025/04/13 02:34:02 rin Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2011 Jared D. McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: ld_thunkbus.c,v 1.34 2025/04/13 02:34:02 rin Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/proc.h>
     34 #include <sys/systm.h>
     35 #include <sys/device.h>
     36 #include <sys/buf.h>
     37 #include <sys/bufq.h>
     38 #include <sys/disk.h>
     39 #include <sys/kmem.h>
     40 
     41 #include <dev/ldvar.h>
     42 
     43 #include <machine/mainbus.h>
     44 #include <machine/thunk.h>
     45 #include <machine/intr.h>
     46 
     47 static int	ld_thunkbus_match(device_t, cfdata_t, void *);
     48 static void	ld_thunkbus_attach(device_t, device_t, void *);
     49 
     50 static int	ld_thunkbus_ldstart(struct ld_softc *, struct buf *);
     51 static int	ld_thunkbus_lddump(struct ld_softc *, void *, daddr_t, int);
     52 static int	ld_thunkbus_ioctl(struct ld_softc *, u_long, void *, int32_t, bool);
     53 
     54 //#define LD_USE_AIO
     55 
     56 #ifdef LD_USE_AIO
     57 static int	ld_aio_sig(void *);
     58 #endif
     59 static void	ld_thunkbus_complete(void *arg);
     60 
     61 struct ld_thunkbus_softc;
     62 
     63 struct ld_thunkbus_transfer {
     64 	struct ld_thunkbus_softc *tt_sc;
     65 	struct aiocb	tt_aio;
     66 	struct buf	*tt_bp;
     67 };
     68 
     69 struct ld_thunkbus_softc {
     70 	struct ld_softc	sc_ld;
     71 
     72 	int		sc_fd;
     73 	void		*sc_ih;
     74 	void		*sc_aio_ih;
     75 
     76 	struct ld_thunkbus_transfer sc_tt;
     77 	bool		busy;
     78 };
     79 
     80 CFATTACH_DECL_NEW(ld_thunkbus, sizeof(struct ld_thunkbus_softc),
     81     ld_thunkbus_match, ld_thunkbus_attach, NULL, NULL);
     82 
     83 static int
     84 ld_thunkbus_match(device_t parent, cfdata_t match, void *opaque)
     85 {
     86 	struct thunkbus_attach_args *taa = opaque;
     87 
     88 	if (taa->taa_type != THUNKBUS_TYPE_DISKIMAGE)
     89 		return 0;
     90 
     91 	return 1;
     92 }
     93 
     94 static void
     95 ld_thunkbus_attach(device_t parent, device_t self, void *opaque)
     96 {
     97 	struct ld_thunkbus_softc *sc = device_private(self);
     98 	struct ld_softc *ld = &sc->sc_ld;
     99 	struct thunkbus_attach_args *taa = opaque;
    100 	const char *path = taa->u.diskimage.path;
    101 	ssize_t blksize;
    102 	off_t size;
    103 
    104 	ld->sc_dv = self;
    105 
    106 	sc->sc_fd = thunk_open(path, O_RDWR, 0);
    107 	if (sc->sc_fd == -1) {
    108 		aprint_error(": couldn't open %s: %d\n", path, thunk_geterrno());
    109 		return;
    110 	}
    111 	if (thunk_fstat_getsize(sc->sc_fd, &size, &blksize) == -1) {
    112 		aprint_error(": couldn't stat %s: %d\n", path, thunk_geterrno());
    113 		return;
    114 	}
    115 
    116 	aprint_naive("\n");
    117 	aprint_normal(": %s (%lld)\n", path, (long long)size);
    118 
    119 	ld->sc_flags = LDF_ENABLED;
    120 	ld->sc_maxxfer = MAXPHYS;
    121 	ld->sc_secsize = 512;
    122 	ld->sc_secperunit = size / ld->sc_secsize;
    123 	ld->sc_maxqueuecnt = 1;
    124 	ld->sc_start = ld_thunkbus_ldstart;
    125 	ld->sc_dump = ld_thunkbus_lddump;
    126 	ld->sc_ioctl = ld_thunkbus_ioctl;
    127 
    128 	sc->sc_ih = softint_establish(SOFTINT_BIO,
    129 	    ld_thunkbus_complete, ld);
    130 
    131 #ifdef LD_USE_AIO
    132 	sc->sc_aio_ih = sigio_intr_establish(ld_aio_sig, sc);
    133 	if (sc->sc_aio_ih == NULL)
    134 		panic("couldn't establish aio sig interrupt");
    135 #endif
    136 
    137 	sc->busy = false;
    138 
    139 	ldattach(ld, BUFQ_DISK_DEFAULT_STRAT);
    140 }
    141 
    142 #ifdef LD_USE_AIO
    143 static int
    144 ld_aio_sig(void *arg)
    145 {
    146 	struct ld_softc *ld = arg;
    147 	struct ld_thunkbus_softc *sc = (struct ld_thunkbus_softc *)ld;
    148 
    149 	softint_schedule(sc->sc_ih);
    150 
    151 	return 0;
    152 }
    153 
    154 static int
    155 ld_thunkbus_ldstart(struct ld_softc *ld, struct buf *bp)
    156 {
    157 	struct ld_thunkbus_softc *sc = (struct ld_thunkbus_softc *)ld;
    158 	struct ld_thunkbus_transfer *tt = &sc->sc_tt;
    159 	off_t offset = bp->b_rawblkno * ld->sc_secsize;
    160 	off_t disksize = ld->sc_secsize * ld->sc_secperunit;
    161 	int error;
    162 
    163 	tt->tt_sc = sc;
    164 	tt->tt_bp = bp;
    165 
    166 	memset(&tt->tt_aio, 0, sizeof(tt->tt_aio));
    167 	tt->tt_aio.aio_fildes = sc->sc_fd;
    168 	tt->tt_aio.aio_buf = bp->b_data;
    169 	tt->tt_aio.aio_nbytes = bp->b_bcount;
    170 	tt->tt_aio.aio_offset = offset;
    171 
    172 	tt->tt_aio.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
    173 	tt->tt_aio.aio_sigevent.sigev_signo = SIGIO;
    174 	tt->tt_aio.aio_sigevent.sigev_value.sival_ptr = tt;
    175 #if 0
    176 	device_printf(sc->sc_ld.sc_dv, "%s addr %p, off=%lld, count=%lld\n",
    177 	    (bp->b_flags & B_READ) ? "rd" : "wr",
    178 	    bp->b_data,
    179 	    (long long)bp->b_rawblkno,
    180 	    (long long)bp->b_bcount);
    181 #endif
    182 	if (sc->busy)
    183 		panic("%s: reentry", __func__);
    184 
    185 	if ((offset < 0) || (offset + bp->b_bcount > disksize)) {
    186 		error = EIO;
    187 		bp->b_error = error;
    188 		bp->b_resid = bp->b_bcount;
    189 	} else {
    190 		sc->busy = true;
    191 		if (bp->b_flags & B_READ)
    192 			error = thunk_aio_read(&tt->tt_aio);
    193 		else
    194 			error = thunk_aio_write(&tt->tt_aio);
    195 	}
    196 	return error;
    197 }
    198 
    199 static void
    200 ld_thunkbus_complete(void *arg)
    201 {
    202 	struct ld_softc *ld = arg;
    203 	struct ld_thunkbus_softc *sc = (struct ld_thunkbus_softc *)ld;
    204 	struct ld_thunkbus_transfer *tt = &sc->sc_tt;
    205 	struct buf *bp = tt->tt_bp;
    206 	int error;
    207 
    208 	/*
    209 	 * check if our aio has finished, we could be called for whatever
    210 	 * reason, for whatever SIGIO since signals can be missed.
    211 	 */
    212 	if (!sc->busy)
    213 		return;
    214 
    215 	/* check if it was OUR sigio */
    216 	error = thunk_aio_error(&tt->tt_aio);
    217 	if (error == EINPROGRESS)
    218 		return;
    219 
    220 	/* use the result */
    221 	if ((error == 0) &&
    222 	    thunk_aio_return(&tt->tt_aio) != -1) {
    223 		bp->b_resid = 0;
    224 	} else {
    225 		bp->b_error = error;
    226 		bp->b_resid = bp->b_bcount;
    227 	}
    228 
    229 	thunk_printf_debug("\tfin\n");
    230 	if (bp->b_error)
    231 		thunk_printf_debug("error!\n");
    232 
    233 	sc->busy = false;
    234 	lddone(&sc->sc_ld, bp);
    235 }
    236 
    237 
    238 #else /* LD_USE_AIO */
    239 
    240 
    241 static int
    242 ld_thunkbus_ldstart(struct ld_softc *ld, struct buf *bp)
    243 {
    244 	struct ld_thunkbus_softc *sc = (struct ld_thunkbus_softc *)ld;
    245 	struct ld_thunkbus_transfer *tt = &sc->sc_tt;
    246 
    247 	tt->tt_sc = sc;
    248 	tt->tt_bp = bp;
    249 
    250 	/* let the softint do the work */
    251 	sc->busy = true;
    252 	softint_schedule(sc->sc_ih);
    253 
    254 	return 0;
    255 }
    256 
    257 static void
    258 ld_thunkbus_complete(void *arg)
    259 {
    260 	struct ld_softc *ld = arg;
    261 	struct ld_thunkbus_softc *sc = (struct ld_thunkbus_softc *)ld;
    262 	struct ld_thunkbus_transfer *tt = &sc->sc_tt;
    263 	struct buf *bp = tt->tt_bp;
    264 	off_t offset = (off_t) bp->b_rawblkno * ld->sc_secsize;
    265 	int64_t ret;
    266 
    267 	if (!sc->busy)
    268 		panic("%s: but not busy?\n", __func__);
    269 
    270 	//printf("%s: %s %u @ %lld -> %p (flags 0x%08x)\n", __func__,
    271 	//    bp->b_flags & B_READ ? "read" : "write",
    272 	//    (unsigned int)bp->b_bcount, (long long)offset, bp->b_data, bp->b_flags);
    273 
    274 	/* this is silly, but better make sure */
    275 	thunk_assert_presence((vaddr_t) bp->b_data, (size_t) bp->b_bcount);
    276 
    277 	/* read/write the request */
    278 	if (bp->b_flags & B_READ) {
    279 		ret = thunk_pread(sc->sc_fd, bp->b_data, bp->b_bcount, offset);
    280 	} else {
    281 		ret = thunk_pwrite(sc->sc_fd, bp->b_data, bp->b_bcount, offset);
    282 	}
    283 
    284 	//if (ret == -1)
    285 	//	printf("%s: errno = %d\n", __func__, thunk_geterrno());
    286 
    287 	/* setup return params */
    288 	if ((ret >= 0) && (ret == bp->b_bcount)) {
    289 		bp->b_resid = 0;
    290 	} else {
    291 		// printf("ret = %d, errno %d?\n",(int) ret, thunk_geterrno());
    292 		bp->b_error = thunk_geterrno();
    293 		bp->b_resid = bp->b_bcount;
    294 	}
    295 	thunk_printf_debug("\tfin\n");
    296 	if (bp->b_error)
    297 		thunk_printf_debug("error!\n");
    298 
    299 	sc->busy = false;
    300 	lddone(&sc->sc_ld, bp);
    301 }
    302 
    303 #endif /* LD_USE_AIO */
    304 
    305 
    306 static int
    307 ld_thunkbus_lddump(struct ld_softc *ld, void *data, daddr_t blkno, int blkcnt)
    308 {
    309 	struct ld_thunkbus_softc *sc = (struct ld_thunkbus_softc *)ld;
    310 	ssize_t len;
    311 
    312 	len = thunk_pwrite(sc->sc_fd, data, blkcnt, blkno * ld->sc_secsize);
    313 	if (len == -1)
    314 		return thunk_geterrno();
    315 	else if (len != blkcnt) {
    316 		device_printf(ld->sc_dv, "%s failed (short xfer)\n", __func__);
    317 		return EIO;
    318 	}
    319 
    320 	return 0;
    321 }
    322 
    323 
    324 static int
    325 ld_thunkbus_ioctl(struct ld_softc *ld, u_long cmd, void *addr, int32_t flag,
    326     bool poll)
    327 {
    328 	struct ld_thunkbus_softc *sc = (struct ld_thunkbus_softc *)ld;
    329 
    330 	switch (cmd) {
    331 	case DIOCCACHESYNC:
    332 		if (thunk_fsync(sc->sc_fd) == -1)
    333 			return thunk_geterrno();
    334 		return 0;
    335 	default:
    336 		return EPASSTHROUGH;
    337 	}
    338 }
    339