1/*-
2 * Copyright (c) 2015-2016 Ruslan Bukin <br@bsdpad.com>
3 * All rights reserved.
4 *
5 * Portions of this software were developed by SRI International and the
6 * University of Cambridge Computer Laboratory under DARPA/AFRL contract
7 * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
8 *
9 * Portions of this software were developed by the University of Cambridge
10 * Computer Laboratory as part of the CTSRD Project, with support from the
11 * UK Higher Education Innovation Fund (HEIF).
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: releng/11.0/sys/riscv/htif/htif_block.c 298476 2016-04-22 15:12:05Z br $");
37
38#include <sys/param.h>
39#include <sys/systm.h>
40#include <sys/kernel.h>
41#include <sys/kthread.h>
42#include <sys/selinfo.h>
43#include <sys/module.h>
44#include <sys/lock.h>
45#include <sys/mutex.h>
46#include <sys/malloc.h>
47#include <sys/sysctl.h>
48#include <sys/uio.h>
49
50#include <sys/bio.h>
51#include <sys/bus.h>
52#include <sys/conf.h>
53#include <sys/disk.h>
54#include <geom/geom_disk.h>
55
56#include <vm/vm.h>
57#include <vm/pmap.h>
58
59#include <machine/md_var.h>
60#include <machine/bus.h>
61#include <machine/trap.h>
62#include <sys/rman.h>
63
64#include "htif.h"
65
66#define	SECTOR_SIZE_SHIFT	(9)
67#define	SECTOR_SIZE		(1 << SECTOR_SIZE_SHIFT)
68
69#define	HTIF_BLK_LOCK(_sc)	mtx_lock(&(_sc)->sc_mtx)
70#define	HTIF_BLK_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
71#define	HTIF_BLK_LOCK_INIT(_sc) \
72	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
73	    "htif_blk", MTX_DEF)
74#define	HTIF_BLK_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
75#define	HTIF_BLK_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
76#define	HTIF_BLK_ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
77
78static void htif_blk_task(void *arg);
79
80static disk_open_t	htif_blk_open;
81static disk_close_t	htif_blk_close;
82static disk_strategy_t	htif_blk_strategy;
83
84struct htif_blk_softc {
85	device_t	dev;
86	struct disk	*disk;
87	struct mtx	htif_io_mtx;
88	struct mtx	sc_mtx;
89	struct proc	*p;
90	struct bio_queue_head bio_queue;
91	int		running;
92	int		intr_chan;
93	int		cmd_done;
94	int		index;
95	uint16_t	curtag;
96};
97
98struct htif_blk_request {
99	uint64_t addr;
100	uint64_t offset;	/* offset in bytes */
101	uint64_t size;		/* length in bytes */
102	uint64_t tag;
103};
104
105static void
106htif_blk_intr(void *arg, uint64_t entry)
107{
108	struct htif_blk_softc *sc;
109	uint64_t devcmd;
110	uint64_t data;
111
112	sc = arg;
113
114	devcmd = HTIF_DEV_CMD(entry);
115	data = HTIF_DEV_DATA(entry);
116
117	if (sc->curtag == data) {
118		wmb();
119		sc->cmd_done = 1;
120		wakeup(&sc->intr_chan);
121	} else {
122		device_printf(sc->dev, "Unexpected tag %d (should be %d)\n",
123		    data, sc->curtag);
124	}
125}
126
127static int
128htif_blk_probe(device_t dev)
129{
130
131	return (0);
132}
133
134static int
135htif_blk_attach(device_t dev)
136{
137	struct htif_blk_softc *sc;
138	char prefix[] = " size=";
139	char *str;
140	long size;
141
142	sc = device_get_softc(dev);
143	sc->dev = dev;
144
145	mtx_init(&sc->htif_io_mtx, device_get_nameunit(dev), "htif_blk", MTX_DEF);
146	HTIF_BLK_LOCK_INIT(sc);
147
148	str = strstr(htif_get_id(dev), prefix);
149
150	size = strtol((str + 6), NULL, 10);
151	if (size == 0) {
152		return (ENXIO);
153	}
154
155	sc->index = htif_get_index(dev);
156	if (sc->index < 0)
157		return (EINVAL);
158	htif_setup_intr(sc->index, htif_blk_intr, sc);
159
160	sc->disk = disk_alloc();
161	sc->disk->d_drv1 = sc;
162
163	sc->disk->d_maxsize = 4096; /* Max transfer */
164	sc->disk->d_name = "htif_blk";
165	sc->disk->d_open = htif_blk_open;
166	sc->disk->d_close = htif_blk_close;
167	sc->disk->d_strategy = htif_blk_strategy;
168	sc->disk->d_unit = 0;
169	sc->disk->d_sectorsize = SECTOR_SIZE;
170	sc->disk->d_mediasize = size;
171	disk_create(sc->disk, DISK_VERSION);
172
173	bioq_init(&sc->bio_queue);
174
175	sc->running = 1;
176
177	kproc_create(&htif_blk_task, sc, &sc->p, 0, 0, "%s: transfer",
178	    device_get_nameunit(dev));
179
180	return (0);
181}
182
183static int
184htif_blk_open(struct disk *dp)
185{
186
187	return (0);
188}
189
190static int
191htif_blk_close(struct disk *dp)
192{
193
194	return (0);
195}
196
197static void
198htif_blk_task(void *arg)
199{
200	struct htif_blk_request req __aligned(HTIF_ALIGN);
201	struct htif_blk_softc *sc;
202	uint64_t req_paddr;
203	struct bio *bp;
204	uint64_t paddr;
205	uint64_t cmd;
206	int i;
207
208	sc = (struct htif_blk_softc *)arg;
209
210	while (1) {
211		HTIF_BLK_LOCK(sc);
212		do {
213			bp = bioq_takefirst(&sc->bio_queue);
214			if (bp == NULL)
215				msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
216		} while (bp == NULL);
217		HTIF_BLK_UNLOCK(sc);
218
219		if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
220			HTIF_BLK_LOCK(sc);
221
222			rmb();
223			req.offset = (bp->bio_pblkno * sc->disk->d_sectorsize);
224			req.size = bp->bio_bcount;
225			paddr = vtophys(bp->bio_data);
226			KASSERT(paddr != 0, ("paddr is 0"));
227			req.addr = paddr;
228			sc->curtag++;
229			req.tag = sc->curtag;
230
231			cmd = sc->index;
232			cmd <<= HTIF_DEV_ID_SHIFT;
233			if (bp->bio_cmd == BIO_READ)
234				cmd |= (HTIF_CMD_READ << HTIF_CMD_SHIFT);
235			else
236				cmd |= (HTIF_CMD_WRITE << HTIF_CMD_SHIFT);
237			req_paddr = vtophys(&req);
238			KASSERT(req_paddr != 0, ("req_paddr is 0"));
239			cmd |= req_paddr;
240
241			sc->cmd_done = 0;
242			htif_command(cmd);
243
244			/* Wait for interrupt */
245			i = 0;
246			while (sc->cmd_done == 0) {
247				msleep(&sc->intr_chan, &sc->sc_mtx, PRIBIO, "intr", hz/2);
248
249				if (i++ > 2) {
250					/* TODO: try to re-issue operation on timeout ? */
251					bp->bio_error = EIO;
252					bp->bio_flags |= BIO_ERROR;
253					disk_err(bp, "hard error", -1, 1);
254					break;
255				}
256			}
257			HTIF_BLK_UNLOCK(sc);
258
259			biodone(bp);
260		} else {
261			printf("unknown op %d\n", bp->bio_cmd);
262		}
263	}
264}
265
266static void
267htif_blk_strategy(struct bio *bp)
268{
269	struct htif_blk_softc *sc;
270
271	sc = bp->bio_disk->d_drv1;
272
273	HTIF_BLK_LOCK(sc);
274	if (sc->running > 0) {
275		bioq_disksort(&sc->bio_queue, bp);
276		HTIF_BLK_UNLOCK(sc);
277		wakeup(sc);
278	} else {
279		HTIF_BLK_UNLOCK(sc);
280		biofinish(bp, NULL, ENXIO);
281	}
282}
283
284static device_method_t htif_blk_methods[] = {
285	DEVMETHOD(device_probe,		htif_blk_probe),
286	DEVMETHOD(device_attach,	htif_blk_attach),
287};
288
289static driver_t htif_blk_driver = {
290	"htif_blk",
291	htif_blk_methods,
292	sizeof(struct htif_blk_softc)
293};
294
295static devclass_t	htif_blk_devclass;
296
297DRIVER_MODULE(htif_blk, htif, htif_blk_driver, htif_blk_devclass, 0, 0);
298