xref: /minix3/minix/drivers/storage/virtio_blk/virtio_blk.c (revision dc2c582f364d955cff3a86092716702b099d2db2)
1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc  * virtio block driver for MINIX 3
3433d6423SLionel Sambuc  *
4433d6423SLionel Sambuc  * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
5433d6423SLionel Sambuc  *
6433d6423SLionel Sambuc  * This software is released under the BSD license. See the LICENSE file
7433d6423SLionel Sambuc  * included in the main directory of this source distribution for the
8433d6423SLionel Sambuc  * license terms and conditions.
9433d6423SLionel Sambuc  */
10433d6423SLionel Sambuc 
11433d6423SLionel Sambuc #include <assert.h>
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc #include <minix/drivers.h>
14433d6423SLionel Sambuc #include <minix/blockdriver_mt.h>
15433d6423SLionel Sambuc #include <minix/drvlib.h>
16433d6423SLionel Sambuc #include <minix/virtio.h>
17433d6423SLionel Sambuc #include <minix/sysutil.h>
18433d6423SLionel Sambuc 
19433d6423SLionel Sambuc #include <sys/ioc_disk.h>
20433d6423SLionel Sambuc 
21433d6423SLionel Sambuc #include "virtio_blk.h"
22433d6423SLionel Sambuc 
23433d6423SLionel Sambuc #define mystatus(tid)  (status_vir[(tid)] & 0xFF)
24433d6423SLionel Sambuc 
25433d6423SLionel Sambuc #define dprintf(s) do {						\
26433d6423SLionel Sambuc 	printf("%s: ", name);					\
27433d6423SLionel Sambuc 	printf s;						\
28433d6423SLionel Sambuc 	printf("\n");						\
29433d6423SLionel Sambuc } while (0)
30433d6423SLionel Sambuc 
31433d6423SLionel Sambuc /* Number of threads to use */
32433d6423SLionel Sambuc #define VIRTIO_BLK_NUM_THREADS		4
33433d6423SLionel Sambuc 
34433d6423SLionel Sambuc /* virtio-blk blocksize is always 512 bytes */
35433d6423SLionel Sambuc #define VIRTIO_BLK_BLOCK_SIZE		512
36433d6423SLionel Sambuc 
37433d6423SLionel Sambuc static const char *const name = "virtio-blk";
38433d6423SLionel Sambuc 
39433d6423SLionel Sambuc /* static device handle */
40433d6423SLionel Sambuc static struct virtio_device *blk_dev;
41433d6423SLionel Sambuc 
42433d6423SLionel Sambuc static struct virtio_blk_config blk_config;
43433d6423SLionel Sambuc 
44433d6423SLionel Sambuc struct virtio_feature blkf[] = {
45433d6423SLionel Sambuc 	{ "barrier",	VIRTIO_BLK_F_BARRIER,	0,	0	},
46433d6423SLionel Sambuc 	{ "sizemax",	VIRTIO_BLK_F_SIZE_MAX,	0,	0	},
47433d6423SLionel Sambuc 	{ "segmax",	VIRTIO_BLK_F_SEG_MAX,	0,	0	},
48433d6423SLionel Sambuc 	{ "geometry",	VIRTIO_BLK_F_GEOMETRY,	0,	0	},
49433d6423SLionel Sambuc 	{ "read-only",	VIRTIO_BLK_F_RO,	0,	0	},
50433d6423SLionel Sambuc 	{ "blocksize",	VIRTIO_BLK_F_BLK_SIZE,	0,	0	},
51433d6423SLionel Sambuc 	{ "scsi",	VIRTIO_BLK_F_SCSI,	0,	0	},
52433d6423SLionel Sambuc 	{ "flush",	VIRTIO_BLK_F_FLUSH,	0,	0	},
53433d6423SLionel Sambuc 	{ "topology",	VIRTIO_BLK_F_TOPOLOGY,	0,	0	},
54433d6423SLionel Sambuc 	{ "idbytes",	VIRTIO_BLK_ID_BYTES,	0,	0	}
55433d6423SLionel Sambuc };
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc /* State information */
58433d6423SLionel Sambuc static int spurious_interrupt = 0;
59433d6423SLionel Sambuc static int terminating = 0;
60433d6423SLionel Sambuc static int open_count = 0;
61433d6423SLionel Sambuc 
62433d6423SLionel Sambuc /* Partition magic */
63433d6423SLionel Sambuc struct device part[DEV_PER_DRIVE];
64433d6423SLionel Sambuc struct device subpart[SUB_PER_DRIVE];
65433d6423SLionel Sambuc 
66433d6423SLionel Sambuc /* Headers for requests */
67433d6423SLionel Sambuc static struct virtio_blk_outhdr *hdrs_vir;
68433d6423SLionel Sambuc static phys_bytes hdrs_phys;
69433d6423SLionel Sambuc 
70433d6423SLionel Sambuc /* Status bytes for requests.
71433d6423SLionel Sambuc  *
72433d6423SLionel Sambuc  * Usually a status is only one byte in length, but we need the lowest bit
73433d6423SLionel Sambuc  * to propagate writable. For this reason we take u16_t and use a mask for
74433d6423SLionel Sambuc  * the lower byte later.
75433d6423SLionel Sambuc  */
76433d6423SLionel Sambuc static u16_t *status_vir;
77433d6423SLionel Sambuc static phys_bytes status_phys;
78433d6423SLionel Sambuc 
79433d6423SLionel Sambuc /* Prototypes */
80433d6423SLionel Sambuc static int virtio_blk_open(devminor_t minor, int access);
81433d6423SLionel Sambuc static int virtio_blk_close(devminor_t minor);
82433d6423SLionel Sambuc static ssize_t virtio_blk_transfer(devminor_t minor, int write, u64_t position,
83433d6423SLionel Sambuc 				   endpoint_t endpt, iovec_t *iovec,
84433d6423SLionel Sambuc 				   unsigned int cnt, int flags);
85433d6423SLionel Sambuc static int virtio_blk_ioctl(devminor_t minor, unsigned long req,
86433d6423SLionel Sambuc 	endpoint_t endpt, cp_grant_id_t grant, endpoint_t user_endpt);
87433d6423SLionel Sambuc static struct device * virtio_blk_part(devminor_t minor);
88433d6423SLionel Sambuc static void virtio_blk_geometry(devminor_t minor, struct part_geom *entry);
89433d6423SLionel Sambuc static void virtio_blk_device_intr(void);
90433d6423SLionel Sambuc static void virtio_blk_spurious_intr(void);
91433d6423SLionel Sambuc static void virtio_blk_intr(unsigned int irqs);
92433d6423SLionel Sambuc static int virtio_blk_device(devminor_t minor, device_id_t *id);
93433d6423SLionel Sambuc 
94433d6423SLionel Sambuc static int virtio_blk_flush(void);
95433d6423SLionel Sambuc static void virtio_blk_terminate(void);
96433d6423SLionel Sambuc static void virtio_blk_cleanup(void);
97433d6423SLionel Sambuc static int virtio_blk_status2error(u8_t status);
98433d6423SLionel Sambuc static int virtio_blk_alloc_requests(void);
99433d6423SLionel Sambuc static void virtio_blk_free_requests(void);
100433d6423SLionel Sambuc static int virtio_blk_feature_setup(void);
101433d6423SLionel Sambuc static int virtio_blk_config(void);
102433d6423SLionel Sambuc static int virtio_blk_probe(int skip);
103433d6423SLionel Sambuc 
104433d6423SLionel Sambuc /* libblockdriver driver tab */
105433d6423SLionel Sambuc static struct blockdriver virtio_blk_dtab  = {
106433d6423SLionel Sambuc 	.bdr_type	= BLOCKDRIVER_TYPE_DISK,
107433d6423SLionel Sambuc 	.bdr_open	= virtio_blk_open,
108433d6423SLionel Sambuc 	.bdr_close	= virtio_blk_close,
109433d6423SLionel Sambuc 	.bdr_transfer	= virtio_blk_transfer,
110433d6423SLionel Sambuc 	.bdr_ioctl	= virtio_blk_ioctl,
111433d6423SLionel Sambuc 	.bdr_part	= virtio_blk_part,
112433d6423SLionel Sambuc 	.bdr_geometry	= virtio_blk_geometry,
113433d6423SLionel Sambuc 	.bdr_intr	= virtio_blk_intr,
114433d6423SLionel Sambuc 	.bdr_device	= virtio_blk_device
115433d6423SLionel Sambuc };
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc static int
virtio_blk_open(devminor_t minor,int access)118433d6423SLionel Sambuc virtio_blk_open(devminor_t minor, int access)
119433d6423SLionel Sambuc {
120433d6423SLionel Sambuc 	struct device *dev = virtio_blk_part(minor);
121433d6423SLionel Sambuc 
122433d6423SLionel Sambuc 	/* Check if this device exists */
123433d6423SLionel Sambuc 	if (!dev)
124433d6423SLionel Sambuc 		return ENXIO;
125433d6423SLionel Sambuc 
126433d6423SLionel Sambuc 	/* Read only devices should only be mounted... read-only */
127433d6423SLionel Sambuc 	if ((access & BDEV_W_BIT) &&
128433d6423SLionel Sambuc 	    virtio_host_supports(blk_dev, VIRTIO_BLK_F_RO))
129433d6423SLionel Sambuc 		return EACCES;
130433d6423SLionel Sambuc 
131433d6423SLionel Sambuc 	/* Partition magic when opened the first time or re-opened after
132433d6423SLionel Sambuc 	 * being fully closed
133433d6423SLionel Sambuc 	 */
134433d6423SLionel Sambuc 	if (open_count == 0) {
135433d6423SLionel Sambuc 		memset(part, 0, sizeof(part));
136433d6423SLionel Sambuc 		memset(subpart, 0, sizeof(subpart));
137433d6423SLionel Sambuc 		part[0].dv_size = blk_config.capacity * VIRTIO_BLK_BLOCK_SIZE;
138433d6423SLionel Sambuc 		partition(&virtio_blk_dtab, 0, P_PRIMARY, 0 /* ATAPI */);
139433d6423SLionel Sambuc 		blockdriver_mt_set_workers(0, VIRTIO_BLK_NUM_THREADS);
140433d6423SLionel Sambuc 	}
141433d6423SLionel Sambuc 
142433d6423SLionel Sambuc 	open_count++;
143433d6423SLionel Sambuc 	return OK;
144433d6423SLionel Sambuc }
145433d6423SLionel Sambuc 
146433d6423SLionel Sambuc static int
virtio_blk_close(devminor_t minor)147433d6423SLionel Sambuc virtio_blk_close(devminor_t minor)
148433d6423SLionel Sambuc {
149433d6423SLionel Sambuc 	struct device *dev = virtio_blk_part(minor);
150433d6423SLionel Sambuc 
151433d6423SLionel Sambuc 	/* Check if this device exists */
152433d6423SLionel Sambuc 	if (!dev)
153433d6423SLionel Sambuc 		return ENXIO;
154433d6423SLionel Sambuc 
155433d6423SLionel Sambuc 	if (open_count == 0) {
156433d6423SLionel Sambuc 		dprintf(("Closing one too many times?"));
157433d6423SLionel Sambuc 		return EINVAL;
158433d6423SLionel Sambuc 	}
159433d6423SLionel Sambuc 
160433d6423SLionel Sambuc 	open_count--;
161433d6423SLionel Sambuc 
162433d6423SLionel Sambuc 	/* If fully closed, flush the device and set workes to 1 */
163433d6423SLionel Sambuc 	if (open_count == 0) {
164433d6423SLionel Sambuc 		virtio_blk_flush();
165433d6423SLionel Sambuc 		blockdriver_mt_set_workers(0, 1);
166433d6423SLionel Sambuc 	}
167433d6423SLionel Sambuc 
168433d6423SLionel Sambuc 	/* If supposed to terminate and fully closed, do it! */
169433d6423SLionel Sambuc 	if (terminating && open_count == 0)
170433d6423SLionel Sambuc 		virtio_blk_terminate();
171433d6423SLionel Sambuc 
172433d6423SLionel Sambuc 	return OK;
173433d6423SLionel Sambuc }
174433d6423SLionel Sambuc 
175433d6423SLionel Sambuc static int
prepare_bufs(struct vumap_vir * vir,struct vumap_phys * phys,int cnt,int w)176433d6423SLionel Sambuc prepare_bufs(struct vumap_vir *vir, struct vumap_phys *phys, int cnt, int w)
177433d6423SLionel Sambuc {
178433d6423SLionel Sambuc 	for (int i = 0; i < cnt ; i++) {
179433d6423SLionel Sambuc 
180433d6423SLionel Sambuc 		/* So you gave us a byte aligned buffer? Good job! */
181433d6423SLionel Sambuc 		if (phys[i].vp_addr & 1) {
182433d6423SLionel Sambuc 			dprintf(("byte aligned %08lx", phys[i].vp_addr));
183433d6423SLionel Sambuc 			return EINVAL;
184433d6423SLionel Sambuc 		}
185433d6423SLionel Sambuc 
186433d6423SLionel Sambuc 		/* Check if the buffer is good */
187433d6423SLionel Sambuc 		if (phys[i].vp_size != vir[i].vv_size) {
188433d6423SLionel Sambuc 			dprintf(("Non-contig buf %08lx", phys[i].vp_addr));
189433d6423SLionel Sambuc 			return EINVAL;
190433d6423SLionel Sambuc 		}
191433d6423SLionel Sambuc 
192433d6423SLionel Sambuc 		/* If write, the buffers only need to be read */
193433d6423SLionel Sambuc 		phys[i].vp_addr |= !w;
194433d6423SLionel Sambuc 	}
195433d6423SLionel Sambuc 
196433d6423SLionel Sambuc 	return OK;
197433d6423SLionel Sambuc }
198433d6423SLionel Sambuc 
199433d6423SLionel Sambuc static int
prepare_vir_vec(endpoint_t endpt,struct vumap_vir * vir,iovec_s_t * iv,int cnt,vir_bytes * size)200433d6423SLionel Sambuc prepare_vir_vec(endpoint_t endpt, struct vumap_vir *vir, iovec_s_t *iv,
201433d6423SLionel Sambuc 		int cnt, vir_bytes *size)
202433d6423SLionel Sambuc {
203433d6423SLionel Sambuc 	/* This is pretty much the same as sum_iovec from AHCI,
204433d6423SLionel Sambuc 	 * except that we don't support any iovecs where the size
205433d6423SLionel Sambuc 	 * is not a multiple of 512
206433d6423SLionel Sambuc 	 */
207433d6423SLionel Sambuc 	vir_bytes s, total = 0;
208433d6423SLionel Sambuc 	for (int i = 0; i < cnt; i++) {
209433d6423SLionel Sambuc 		s = iv[i].iov_size;
210433d6423SLionel Sambuc 
211433d6423SLionel Sambuc 		if (s == 0 || (s % VIRTIO_BLK_BLOCK_SIZE) || s > LONG_MAX) {
212433d6423SLionel Sambuc 			dprintf(("bad iv[%d].iov_size (%lu) from %d", i, s,
213433d6423SLionel Sambuc 								      endpt));
214433d6423SLionel Sambuc 			return EINVAL;
215433d6423SLionel Sambuc 		}
216433d6423SLionel Sambuc 
217433d6423SLionel Sambuc 		total += s;
218433d6423SLionel Sambuc 
219433d6423SLionel Sambuc 		if (total > LONG_MAX) {
220433d6423SLionel Sambuc 			dprintf(("total overflow from %d", endpt));
221433d6423SLionel Sambuc 			return EINVAL;
222433d6423SLionel Sambuc 		}
223433d6423SLionel Sambuc 
224433d6423SLionel Sambuc 		if (endpt == SELF)
225433d6423SLionel Sambuc 			vir[i].vv_addr = (vir_bytes)iv[i].iov_grant;
226433d6423SLionel Sambuc 		else
227433d6423SLionel Sambuc 			vir[i].vv_grant = iv[i].iov_grant;
228433d6423SLionel Sambuc 
229433d6423SLionel Sambuc 		vir[i].vv_size = iv[i].iov_size;
230433d6423SLionel Sambuc 
231433d6423SLionel Sambuc 	}
232433d6423SLionel Sambuc 
233433d6423SLionel Sambuc 	*size = total;
234433d6423SLionel Sambuc 	return OK;
235433d6423SLionel Sambuc }
236433d6423SLionel Sambuc 
237433d6423SLionel Sambuc static ssize_t
virtio_blk_transfer(devminor_t minor,int write,u64_t position,endpoint_t endpt,iovec_t * iovec,unsigned int cnt,int flags)238433d6423SLionel Sambuc virtio_blk_transfer(devminor_t minor, int write, u64_t position,
239433d6423SLionel Sambuc 		    endpoint_t endpt, iovec_t *iovec, unsigned int cnt,
240433d6423SLionel Sambuc 		    int flags)
241433d6423SLionel Sambuc {
242433d6423SLionel Sambuc 	/* Need to translate vir to phys */
243433d6423SLionel Sambuc 	struct vumap_vir vir[NR_IOREQS];
244433d6423SLionel Sambuc 
245433d6423SLionel Sambuc 	/* Physical addresses of buffers, including header and trailer */
246433d6423SLionel Sambuc 	struct vumap_phys phys[NR_IOREQS + 2];
247433d6423SLionel Sambuc 
248433d6423SLionel Sambuc 	/* Which thread is doing the transfer? */
249433d6423SLionel Sambuc 	thread_id_t tid = blockdriver_mt_get_tid();
250433d6423SLionel Sambuc 
251433d6423SLionel Sambuc 	vir_bytes size = 0;
252433d6423SLionel Sambuc 	vir_bytes size_tmp = 0;
253433d6423SLionel Sambuc 	struct device *dv;
254433d6423SLionel Sambuc 	u64_t sector;
255433d6423SLionel Sambuc 	u64_t end_part;
256433d6423SLionel Sambuc 	int r, pcnt = sizeof(phys) / sizeof(phys[0]);
257433d6423SLionel Sambuc 
258433d6423SLionel Sambuc 	iovec_s_t *iv = (iovec_s_t *)iovec;
259433d6423SLionel Sambuc 	int access = write ? VUA_READ : VUA_WRITE;
260433d6423SLionel Sambuc 
261433d6423SLionel Sambuc 	/* Make sure we don't touch this one anymore */
262433d6423SLionel Sambuc 	iovec = NULL;
263433d6423SLionel Sambuc 
264433d6423SLionel Sambuc 	if (cnt > NR_IOREQS)
265433d6423SLionel Sambuc 		return EINVAL;
266433d6423SLionel Sambuc 
267433d6423SLionel Sambuc 	/* position greater than capacity? */
268433d6423SLionel Sambuc 	if (position >= blk_config.capacity * VIRTIO_BLK_BLOCK_SIZE)
269433d6423SLionel Sambuc 		return 0;
270433d6423SLionel Sambuc 
271433d6423SLionel Sambuc 	dv = virtio_blk_part(minor);
272433d6423SLionel Sambuc 
273433d6423SLionel Sambuc 	/* Does device exist? */
274433d6423SLionel Sambuc 	if (!dv)
275433d6423SLionel Sambuc 		return ENXIO;
276433d6423SLionel Sambuc 
277433d6423SLionel Sambuc 	position += dv->dv_base;
278433d6423SLionel Sambuc 	end_part = dv->dv_base + dv->dv_size;
279433d6423SLionel Sambuc 
280433d6423SLionel Sambuc 	/* Hmmm, AHCI tries to fix this up, but lets just say everything
281433d6423SLionel Sambuc 	 * needs to be sector (512 byte) aligned...
282433d6423SLionel Sambuc 	 */
283433d6423SLionel Sambuc 	if (position % VIRTIO_BLK_BLOCK_SIZE) {
284433d6423SLionel Sambuc 		dprintf(("Non sector-aligned access %016llx", position));
285433d6423SLionel Sambuc 		return EINVAL;
286433d6423SLionel Sambuc 	}
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc 	sector = position / VIRTIO_BLK_BLOCK_SIZE;
289433d6423SLionel Sambuc 
290433d6423SLionel Sambuc 	r = prepare_vir_vec(endpt, vir, iv, cnt, &size);
291433d6423SLionel Sambuc 
292433d6423SLionel Sambuc 	if (r != OK)
293433d6423SLionel Sambuc 		return r;
294433d6423SLionel Sambuc 
295433d6423SLionel Sambuc 	if (position >= end_part)
296433d6423SLionel Sambuc 		return 0;
297433d6423SLionel Sambuc 
298433d6423SLionel Sambuc 	/* Truncate if the partition is smaller than that */
299433d6423SLionel Sambuc 	if (position + size > end_part - 1) {
300433d6423SLionel Sambuc 		size = end_part - position;
301433d6423SLionel Sambuc 
302433d6423SLionel Sambuc 		/* Fix up later */
303433d6423SLionel Sambuc 		size_tmp = 0;
304433d6423SLionel Sambuc 		cnt = 0;
305433d6423SLionel Sambuc 	} else {
306433d6423SLionel Sambuc 		/* Use all buffers */
307433d6423SLionel Sambuc 		size_tmp = size;
308433d6423SLionel Sambuc 	}
309433d6423SLionel Sambuc 
310433d6423SLionel Sambuc 	/* Fix up the number of vectors if size was truncated */
311433d6423SLionel Sambuc 	while (size_tmp < size)
312433d6423SLionel Sambuc 		size_tmp += vir[cnt++].vv_size;
313433d6423SLionel Sambuc 
314433d6423SLionel Sambuc 	/* If the last vector was too big, just truncate it */
315433d6423SLionel Sambuc 	if (size_tmp > size) {
316433d6423SLionel Sambuc 		vir[cnt - 1].vv_size = vir[cnt -1].vv_size - (size_tmp - size);
317433d6423SLionel Sambuc 		size_tmp -= (size_tmp - size);
318433d6423SLionel Sambuc 	}
319433d6423SLionel Sambuc 
320433d6423SLionel Sambuc 	if (size % VIRTIO_BLK_BLOCK_SIZE) {
321433d6423SLionel Sambuc 		dprintf(("non-sector sized read (%lu) from %d", size, endpt));
322433d6423SLionel Sambuc 		return EINVAL;
323433d6423SLionel Sambuc 	}
324433d6423SLionel Sambuc 
325433d6423SLionel Sambuc 	/* Map vir to phys */
326433d6423SLionel Sambuc 	if ((r = sys_vumap(endpt, vir, cnt, 0, access,
327433d6423SLionel Sambuc 			   &phys[1], &pcnt)) != OK) {
328433d6423SLionel Sambuc 
329433d6423SLionel Sambuc 		dprintf(("Unable to map memory from %d (%d)", endpt, r));
330433d6423SLionel Sambuc 		return r;
331433d6423SLionel Sambuc 	}
332433d6423SLionel Sambuc 
333433d6423SLionel Sambuc 	/* Prepare the header */
334433d6423SLionel Sambuc 	memset(&hdrs_vir[tid], 0, sizeof(hdrs_vir[0]));
335433d6423SLionel Sambuc 
336433d6423SLionel Sambuc 	if (write)
337433d6423SLionel Sambuc 		hdrs_vir[tid].type = VIRTIO_BLK_T_OUT;
338433d6423SLionel Sambuc 	else
339433d6423SLionel Sambuc 		hdrs_vir[tid].type = VIRTIO_BLK_T_IN;
340433d6423SLionel Sambuc 
341433d6423SLionel Sambuc 	hdrs_vir[tid].ioprio = 0;
342433d6423SLionel Sambuc 	hdrs_vir[tid].sector = sector;
343433d6423SLionel Sambuc 
344433d6423SLionel Sambuc 	/* First the header */
345433d6423SLionel Sambuc 	phys[0].vp_addr = hdrs_phys + tid * sizeof(hdrs_vir[0]);
346433d6423SLionel Sambuc 	phys[0].vp_size = sizeof(hdrs_vir[0]);
347433d6423SLionel Sambuc 
348433d6423SLionel Sambuc 	/* Put the physical buffers into phys */
349433d6423SLionel Sambuc 	if ((r = prepare_bufs(vir, &phys[1], pcnt, write)) != OK)
350433d6423SLionel Sambuc 		return r;
351433d6423SLionel Sambuc 
352433d6423SLionel Sambuc 	/* Put the status at the end */
353433d6423SLionel Sambuc 	phys[pcnt + 1].vp_addr = status_phys + tid * sizeof(status_vir[0]);
354433d6423SLionel Sambuc 	phys[pcnt + 1].vp_size = sizeof(u8_t);
355433d6423SLionel Sambuc 
356433d6423SLionel Sambuc 	/* Status always needs write access */
357433d6423SLionel Sambuc 	phys[1 + pcnt].vp_addr |= 1;
358433d6423SLionel Sambuc 
359433d6423SLionel Sambuc 	/* Send addresses to queue */
360433d6423SLionel Sambuc 	virtio_to_queue(blk_dev, 0, phys, 2 + pcnt, &tid);
361433d6423SLionel Sambuc 
362433d6423SLionel Sambuc 	/* Wait for completion */
363433d6423SLionel Sambuc 	blockdriver_mt_sleep();
364433d6423SLionel Sambuc 
365433d6423SLionel Sambuc 	/* All was good */
366433d6423SLionel Sambuc 	if (mystatus(tid) == VIRTIO_BLK_S_OK)
367433d6423SLionel Sambuc 		return size;
368433d6423SLionel Sambuc 
369433d6423SLionel Sambuc 	/* Error path */
370433d6423SLionel Sambuc 	dprintf(("ERROR status=%02x sector=%llu len=%lx cnt=%d op=%s t=%d",
371433d6423SLionel Sambuc 		 mystatus(tid), sector, size, pcnt,
372433d6423SLionel Sambuc 		 write ? "write" : "read", tid));
373433d6423SLionel Sambuc 
374433d6423SLionel Sambuc 	return virtio_blk_status2error(mystatus(tid));
375433d6423SLionel Sambuc }
376433d6423SLionel Sambuc 
377433d6423SLionel Sambuc static int
virtio_blk_ioctl(devminor_t minor,unsigned long req,endpoint_t endpt,cp_grant_id_t grant,endpoint_t UNUSED (user_endpt))378433d6423SLionel Sambuc virtio_blk_ioctl(devminor_t minor, unsigned long req, endpoint_t endpt,
379433d6423SLionel Sambuc 		 cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
380433d6423SLionel Sambuc {
381433d6423SLionel Sambuc 	switch (req) {
382433d6423SLionel Sambuc 
383433d6423SLionel Sambuc 	case DIOCOPENCT:
384433d6423SLionel Sambuc 		return sys_safecopyto(endpt, grant, 0,
385433d6423SLionel Sambuc 			(vir_bytes) &open_count, sizeof(open_count));
386433d6423SLionel Sambuc 
387433d6423SLionel Sambuc 	case DIOCFLUSH:
388433d6423SLionel Sambuc 		return virtio_blk_flush();
389433d6423SLionel Sambuc 
390433d6423SLionel Sambuc 	}
391433d6423SLionel Sambuc 
392433d6423SLionel Sambuc 	return ENOTTY;
393433d6423SLionel Sambuc }
394433d6423SLionel Sambuc 
395433d6423SLionel Sambuc static struct device *
virtio_blk_part(devminor_t minor)396433d6423SLionel Sambuc virtio_blk_part(devminor_t minor)
397433d6423SLionel Sambuc {
398433d6423SLionel Sambuc 	/* There's only a single drive attached to this device, alyways.
399433d6423SLionel Sambuc 	 * Lets take some shortcuts...
400433d6423SLionel Sambuc 	 */
401433d6423SLionel Sambuc 
402433d6423SLionel Sambuc 	/* Take care of d0 d0p0 ... */
403433d6423SLionel Sambuc 	if (minor >= 0 && minor < DEV_PER_DRIVE)
404433d6423SLionel Sambuc 		return &part[minor];
405433d6423SLionel Sambuc 
406433d6423SLionel Sambuc 	/* subparts start at MINOR_d0p0s0 */
407433d6423SLionel Sambuc 	if (minor >= MINOR_d0p0s0) {
408433d6423SLionel Sambuc 		minor -= MINOR_d0p0s0;
409433d6423SLionel Sambuc 
410433d6423SLionel Sambuc 		/* Only for the first disk */
411433d6423SLionel Sambuc 		if (minor >= SUB_PER_DRIVE)
412433d6423SLionel Sambuc 			return NULL;
413433d6423SLionel Sambuc 
414433d6423SLionel Sambuc 		return &subpart[minor];
415433d6423SLionel Sambuc 	}
416433d6423SLionel Sambuc 
417433d6423SLionel Sambuc 	return NULL;
418433d6423SLionel Sambuc }
419433d6423SLionel Sambuc 
420433d6423SLionel Sambuc static void
virtio_blk_geometry(devminor_t minor,struct part_geom * entry)421433d6423SLionel Sambuc virtio_blk_geometry(devminor_t minor, struct part_geom *entry)
422433d6423SLionel Sambuc {
423433d6423SLionel Sambuc 	/* Only for the drive */
424433d6423SLionel Sambuc 	if (minor != 0)
425433d6423SLionel Sambuc 		return;
426433d6423SLionel Sambuc 
427433d6423SLionel Sambuc 	/* Only if the host supports it */
428433d6423SLionel Sambuc 	if(!virtio_host_supports(blk_dev, VIRTIO_BLK_F_GEOMETRY))
429433d6423SLionel Sambuc 		return;
430433d6423SLionel Sambuc 
431433d6423SLionel Sambuc 	entry->cylinders = blk_config.geometry.cylinders;
432433d6423SLionel Sambuc 	entry->heads = blk_config.geometry.heads;
433433d6423SLionel Sambuc 	entry->sectors = blk_config.geometry.sectors;
434433d6423SLionel Sambuc }
435433d6423SLionel Sambuc 
436433d6423SLionel Sambuc static void
virtio_blk_device_intr(void)437433d6423SLionel Sambuc virtio_blk_device_intr(void)
438433d6423SLionel Sambuc {
439433d6423SLionel Sambuc 	thread_id_t *tid;
440433d6423SLionel Sambuc 
441433d6423SLionel Sambuc 	/* Multiple requests might have finished */
442d1db724fSDavid van Moolenbroek 	while (!virtio_from_queue(blk_dev, 0, (void**)&tid, NULL))
443433d6423SLionel Sambuc 		blockdriver_mt_wakeup(*tid);
444433d6423SLionel Sambuc }
445433d6423SLionel Sambuc 
446433d6423SLionel Sambuc static void
virtio_blk_spurious_intr(void)447433d6423SLionel Sambuc virtio_blk_spurious_intr(void)
448433d6423SLionel Sambuc {
449433d6423SLionel Sambuc 	/* Output a single message about spurious interrupts */
450433d6423SLionel Sambuc 	if (spurious_interrupt)
451433d6423SLionel Sambuc 		return;
452433d6423SLionel Sambuc 
453433d6423SLionel Sambuc 	dprintf(("Got spurious interrupt"));
454433d6423SLionel Sambuc 	spurious_interrupt = 1;
455433d6423SLionel Sambuc }
456433d6423SLionel Sambuc 
457433d6423SLionel Sambuc static void
virtio_blk_intr(unsigned int irqs)458433d6423SLionel Sambuc virtio_blk_intr(unsigned int irqs)
459433d6423SLionel Sambuc {
460433d6423SLionel Sambuc 
461433d6423SLionel Sambuc 	if (virtio_had_irq(blk_dev))
462433d6423SLionel Sambuc 		virtio_blk_device_intr();
463433d6423SLionel Sambuc 	else
464433d6423SLionel Sambuc 		virtio_blk_spurious_intr();
465433d6423SLionel Sambuc 
466433d6423SLionel Sambuc 	virtio_irq_enable(blk_dev);
467433d6423SLionel Sambuc }
468433d6423SLionel Sambuc 
469433d6423SLionel Sambuc static int
virtio_blk_device(devminor_t minor,device_id_t * id)470433d6423SLionel Sambuc virtio_blk_device(devminor_t minor, device_id_t *id)
471433d6423SLionel Sambuc {
472433d6423SLionel Sambuc 	struct device *dev = virtio_blk_part(minor);
473433d6423SLionel Sambuc 
474433d6423SLionel Sambuc 	/* Check if this device exists */
475433d6423SLionel Sambuc 	if (!dev)
476433d6423SLionel Sambuc 		return ENXIO;
477433d6423SLionel Sambuc 
478433d6423SLionel Sambuc 	*id = 0;
479433d6423SLionel Sambuc 	return OK;
480433d6423SLionel Sambuc }
481433d6423SLionel Sambuc 
482433d6423SLionel Sambuc static int
virtio_blk_flush(void)483433d6423SLionel Sambuc virtio_blk_flush(void)
484433d6423SLionel Sambuc {
485433d6423SLionel Sambuc 	struct vumap_phys phys[2];
486433d6423SLionel Sambuc 	size_t phys_cnt = sizeof(phys) / sizeof(phys[0]);
487433d6423SLionel Sambuc 
488433d6423SLionel Sambuc 	/* Which thread is doing this request? */
489433d6423SLionel Sambuc 	thread_id_t tid = blockdriver_mt_get_tid();
490433d6423SLionel Sambuc 
491433d6423SLionel Sambuc 	/* Host may not support flushing */
492433d6423SLionel Sambuc 	if (!virtio_host_supports(blk_dev, VIRTIO_BLK_F_FLUSH))
493433d6423SLionel Sambuc 		return EOPNOTSUPP;
494433d6423SLionel Sambuc 
495433d6423SLionel Sambuc 	/* Prepare the header */
496433d6423SLionel Sambuc 	memset(&hdrs_vir[tid], 0, sizeof(hdrs_vir[0]));
497433d6423SLionel Sambuc 	hdrs_vir[tid].type = VIRTIO_BLK_T_FLUSH;
498433d6423SLionel Sambuc 
499433d6423SLionel Sambuc 	/* Let this be a barrier if the host supports it */
500433d6423SLionel Sambuc 	if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_BARRIER))
501433d6423SLionel Sambuc 		hdrs_vir[tid].type |= VIRTIO_BLK_T_BARRIER;
502433d6423SLionel Sambuc 
503433d6423SLionel Sambuc 	/* Header and status for the queue */
504433d6423SLionel Sambuc 	phys[0].vp_addr = hdrs_phys + tid * sizeof(hdrs_vir[0]);
505433d6423SLionel Sambuc 	phys[0].vp_size = sizeof(hdrs_vir[0]);
506433d6423SLionel Sambuc 	phys[1].vp_addr = status_phys + tid * sizeof(status_vir[0]);
507433d6423SLionel Sambuc 	phys[1].vp_size = 1;
508433d6423SLionel Sambuc 
509433d6423SLionel Sambuc 	/* Status always needs write access */
510433d6423SLionel Sambuc 	phys[1].vp_addr |= 1;
511433d6423SLionel Sambuc 
512433d6423SLionel Sambuc 	/* Send flush request to queue */
513433d6423SLionel Sambuc 	virtio_to_queue(blk_dev, 0, phys, phys_cnt, &tid);
514433d6423SLionel Sambuc 
515433d6423SLionel Sambuc 	blockdriver_mt_sleep();
516433d6423SLionel Sambuc 
517433d6423SLionel Sambuc 	/* All was good */
518433d6423SLionel Sambuc 	if (mystatus(tid) == VIRTIO_BLK_S_OK)
519433d6423SLionel Sambuc 		return OK;
520433d6423SLionel Sambuc 
521433d6423SLionel Sambuc 	/* Error path */
522433d6423SLionel Sambuc 	dprintf(("ERROR status=%02x op=flush t=%d", mystatus(tid), tid));
523433d6423SLionel Sambuc 
524433d6423SLionel Sambuc 	return virtio_blk_status2error(mystatus(tid));
525433d6423SLionel Sambuc }
526433d6423SLionel Sambuc 
527433d6423SLionel Sambuc static void
virtio_blk_terminate(void)528433d6423SLionel Sambuc virtio_blk_terminate(void)
529433d6423SLionel Sambuc {
530433d6423SLionel Sambuc 	/* Don't terminate if still opened */
531433d6423SLionel Sambuc 	if (open_count > 0)
532433d6423SLionel Sambuc 		return;
533433d6423SLionel Sambuc 
534433d6423SLionel Sambuc 	blockdriver_mt_terminate();
535433d6423SLionel Sambuc }
536433d6423SLionel Sambuc 
537433d6423SLionel Sambuc static void
virtio_blk_cleanup(void)538433d6423SLionel Sambuc virtio_blk_cleanup(void)
539433d6423SLionel Sambuc {
540433d6423SLionel Sambuc 	/* Just free the memory we allocated */
541433d6423SLionel Sambuc 	virtio_blk_free_requests();
542433d6423SLionel Sambuc 	virtio_reset_device(blk_dev);
543433d6423SLionel Sambuc 	virtio_free_queues(blk_dev);
544433d6423SLionel Sambuc 	virtio_free_device(blk_dev);
545433d6423SLionel Sambuc 	blk_dev = NULL;
546433d6423SLionel Sambuc }
547433d6423SLionel Sambuc 
548433d6423SLionel Sambuc static int
virtio_blk_status2error(u8_t status)549433d6423SLionel Sambuc virtio_blk_status2error(u8_t status)
550433d6423SLionel Sambuc {
551433d6423SLionel Sambuc 	/* Convert a status from the host to an error */
552433d6423SLionel Sambuc 	switch (status) {
553433d6423SLionel Sambuc 		case VIRTIO_BLK_S_IOERR:
554433d6423SLionel Sambuc 			return EIO;
555433d6423SLionel Sambuc 		case VIRTIO_BLK_S_UNSUPP:
556433d6423SLionel Sambuc 			return ENOTSUP;
557433d6423SLionel Sambuc 		default:
558433d6423SLionel Sambuc 			panic("%s: unknown status: %02x", name, status);
559433d6423SLionel Sambuc 	}
560433d6423SLionel Sambuc 	/* Never reached */
561433d6423SLionel Sambuc 	return OK;
562433d6423SLionel Sambuc }
563433d6423SLionel Sambuc 
564433d6423SLionel Sambuc static int
virtio_blk_alloc_requests(void)565433d6423SLionel Sambuc virtio_blk_alloc_requests(void)
566433d6423SLionel Sambuc {
567433d6423SLionel Sambuc 	/* Allocate memory for request headers and status field */
568433d6423SLionel Sambuc 
569433d6423SLionel Sambuc 	hdrs_vir = alloc_contig(VIRTIO_BLK_NUM_THREADS * sizeof(hdrs_vir[0]),
570433d6423SLionel Sambuc 				AC_ALIGN4K, &hdrs_phys);
571433d6423SLionel Sambuc 
572433d6423SLionel Sambuc 	if (!hdrs_vir)
573433d6423SLionel Sambuc 		return ENOMEM;
574433d6423SLionel Sambuc 
575433d6423SLionel Sambuc 	status_vir = alloc_contig(VIRTIO_BLK_NUM_THREADS * sizeof(status_vir[0]),
576433d6423SLionel Sambuc 				  AC_ALIGN4K, &status_phys);
577433d6423SLionel Sambuc 
578433d6423SLionel Sambuc 	if (!status_vir) {
579433d6423SLionel Sambuc 		free_contig(hdrs_vir, VIRTIO_BLK_NUM_THREADS * sizeof(hdrs_vir[0]));
580433d6423SLionel Sambuc 		return ENOMEM;
581433d6423SLionel Sambuc 	}
582433d6423SLionel Sambuc 
583433d6423SLionel Sambuc 	return OK;
584433d6423SLionel Sambuc }
585433d6423SLionel Sambuc 
586433d6423SLionel Sambuc static void
virtio_blk_free_requests(void)587433d6423SLionel Sambuc virtio_blk_free_requests(void)
588433d6423SLionel Sambuc {
589433d6423SLionel Sambuc 	free_contig(hdrs_vir, VIRTIO_BLK_NUM_THREADS * sizeof(hdrs_vir[0]));
590433d6423SLionel Sambuc 	free_contig(status_vir, VIRTIO_BLK_NUM_THREADS * sizeof(status_vir[0]));
591433d6423SLionel Sambuc }
592433d6423SLionel Sambuc 
593433d6423SLionel Sambuc static int
virtio_blk_feature_setup(void)594433d6423SLionel Sambuc virtio_blk_feature_setup(void)
595433d6423SLionel Sambuc {
596433d6423SLionel Sambuc 	/* Feature setup for virtio-blk
597433d6423SLionel Sambuc 	 *
598433d6423SLionel Sambuc 	 * FIXME: Besides the geometry, everything is just debug output
599433d6423SLionel Sambuc 	 * FIXME2: magic numbers
600433d6423SLionel Sambuc 	 */
601433d6423SLionel Sambuc 	if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_SEG_MAX)) {
602433d6423SLionel Sambuc 		blk_config.seg_max = virtio_sread32(blk_dev, 12);
603433d6423SLionel Sambuc 		dprintf(("Seg Max: %d", blk_config.seg_max));
604433d6423SLionel Sambuc 	}
605433d6423SLionel Sambuc 
606433d6423SLionel Sambuc 	if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_GEOMETRY)) {
607433d6423SLionel Sambuc 		blk_config.geometry.cylinders = virtio_sread16(blk_dev, 16);
608433d6423SLionel Sambuc 		blk_config.geometry.heads = virtio_sread8(blk_dev, 18);
609433d6423SLionel Sambuc 		blk_config.geometry.sectors = virtio_sread8(blk_dev, 19);
610433d6423SLionel Sambuc 
611433d6423SLionel Sambuc 		dprintf(("Geometry: cyl=%d heads=%d sectors=%d",
612433d6423SLionel Sambuc 					blk_config.geometry.cylinders,
613433d6423SLionel Sambuc 					blk_config.geometry.heads,
614433d6423SLionel Sambuc 					blk_config.geometry.sectors));
615433d6423SLionel Sambuc 	}
616433d6423SLionel Sambuc 
617433d6423SLionel Sambuc 	if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_SIZE_MAX))
618433d6423SLionel Sambuc 		dprintf(("Has size max"));
619433d6423SLionel Sambuc 
620433d6423SLionel Sambuc 	if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_FLUSH))
621433d6423SLionel Sambuc 		dprintf(("Supports flushing"));
622433d6423SLionel Sambuc 
623433d6423SLionel Sambuc 	if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_BLK_SIZE)) {
624433d6423SLionel Sambuc 		blk_config.blk_size = virtio_sread32(blk_dev, 20);
625433d6423SLionel Sambuc 		dprintf(("Block Size: %d", blk_config.blk_size));
626433d6423SLionel Sambuc 	}
627433d6423SLionel Sambuc 
628433d6423SLionel Sambuc 	if (virtio_host_supports(blk_dev, VIRTIO_BLK_F_BARRIER))
629433d6423SLionel Sambuc 		dprintf(("Supports barrier"));
630433d6423SLionel Sambuc 
631433d6423SLionel Sambuc 	return 0;
632433d6423SLionel Sambuc }
633433d6423SLionel Sambuc 
634433d6423SLionel Sambuc static int
virtio_blk_config(void)635433d6423SLionel Sambuc virtio_blk_config(void)
636433d6423SLionel Sambuc {
637433d6423SLionel Sambuc 	u32_t sectors_low, sectors_high, size_mbs;
638433d6423SLionel Sambuc 
639433d6423SLionel Sambuc 	/* capacity is always there */
640433d6423SLionel Sambuc 	sectors_low = virtio_sread32(blk_dev, 0);
641433d6423SLionel Sambuc 	sectors_high = virtio_sread32(blk_dev, 4);
642433d6423SLionel Sambuc 	blk_config.capacity = ((u64_t)sectors_high << 32) | sectors_low;
643433d6423SLionel Sambuc 
644433d6423SLionel Sambuc 	/* If this gets truncated, you have a big disk... */
645433d6423SLionel Sambuc 	size_mbs = (u32_t)(blk_config.capacity * 512 / 1024 / 1024);
646433d6423SLionel Sambuc 	dprintf(("Capacity: %d MB", size_mbs));
647433d6423SLionel Sambuc 
648433d6423SLionel Sambuc 	/* do feature setup */
649433d6423SLionel Sambuc 	virtio_blk_feature_setup();
650433d6423SLionel Sambuc 	return 0;
651433d6423SLionel Sambuc }
652433d6423SLionel Sambuc 
653433d6423SLionel Sambuc static int
virtio_blk_probe(int skip)654433d6423SLionel Sambuc virtio_blk_probe(int skip)
655433d6423SLionel Sambuc {
656433d6423SLionel Sambuc 	int r;
657433d6423SLionel Sambuc 
658433d6423SLionel Sambuc 	/* sub device id for virtio-blk is 0x0002 */
659433d6423SLionel Sambuc 	blk_dev = virtio_setup_device(0x0002, name, blkf,
660433d6423SLionel Sambuc 				      sizeof(blkf) / sizeof(blkf[0]),
661433d6423SLionel Sambuc 				      VIRTIO_BLK_NUM_THREADS, skip);
662433d6423SLionel Sambuc 	if (!blk_dev)
663433d6423SLionel Sambuc 		return ENXIO;
664433d6423SLionel Sambuc 
665433d6423SLionel Sambuc 	/* virtio-blk has one queue only */
666433d6423SLionel Sambuc 	if ((r = virtio_alloc_queues(blk_dev, 1)) != OK) {
667433d6423SLionel Sambuc 		virtio_free_device(blk_dev);
668433d6423SLionel Sambuc 		return r;
669433d6423SLionel Sambuc 	}
670433d6423SLionel Sambuc 
671433d6423SLionel Sambuc 	/* Allocate memory for headers and status */
672*dc2c582fSDavid van Moolenbroek 	if ((r = virtio_blk_alloc_requests()) != OK) {
673433d6423SLionel Sambuc 		virtio_free_queues(blk_dev);
674433d6423SLionel Sambuc 		virtio_free_device(blk_dev);
675433d6423SLionel Sambuc 		return r;
676433d6423SLionel Sambuc 	}
677433d6423SLionel Sambuc 
678433d6423SLionel Sambuc 	virtio_blk_config();
679433d6423SLionel Sambuc 
680433d6423SLionel Sambuc 	/* Let the host now that we are ready */
681433d6423SLionel Sambuc 	virtio_device_ready(blk_dev);
682433d6423SLionel Sambuc 
683433d6423SLionel Sambuc 	virtio_irq_enable(blk_dev);
684433d6423SLionel Sambuc 
685433d6423SLionel Sambuc 	return OK;
686433d6423SLionel Sambuc }
687433d6423SLionel Sambuc 
688433d6423SLionel Sambuc static int
sef_cb_init_fresh(int type,sef_init_info_t * info)689433d6423SLionel Sambuc sef_cb_init_fresh(int type, sef_init_info_t *info)
690433d6423SLionel Sambuc {
691433d6423SLionel Sambuc 	long instance = 0;
692433d6423SLionel Sambuc 	int r;
693433d6423SLionel Sambuc 
694433d6423SLionel Sambuc 	env_parse("instance", "d", 0, &instance, 0, 255);
695433d6423SLionel Sambuc 
696433d6423SLionel Sambuc 	if ((r = virtio_blk_probe((int)instance)) == OK) {
697433d6423SLionel Sambuc 		blockdriver_announce(type);
698433d6423SLionel Sambuc 		return OK;
699433d6423SLionel Sambuc 	}
700433d6423SLionel Sambuc 
701433d6423SLionel Sambuc 	/* Error path */
702433d6423SLionel Sambuc 	if (r == ENXIO)
703433d6423SLionel Sambuc 		panic("%s: No device found", name);
704433d6423SLionel Sambuc 
705433d6423SLionel Sambuc 	if (r == ENOMEM)
706433d6423SLionel Sambuc 		panic("%s: Not enough memory", name);
707433d6423SLionel Sambuc 
708433d6423SLionel Sambuc 	panic("%s: Unexpected failure (%d)", name, r);
709433d6423SLionel Sambuc }
710433d6423SLionel Sambuc 
711433d6423SLionel Sambuc static void
sef_cb_signal_handler(int signo)712433d6423SLionel Sambuc sef_cb_signal_handler(int signo)
713433d6423SLionel Sambuc {
714433d6423SLionel Sambuc 	/* Ignore all signals but SIGTERM */
715433d6423SLionel Sambuc 	if (signo != SIGTERM)
716433d6423SLionel Sambuc 		return;
717433d6423SLionel Sambuc 
718433d6423SLionel Sambuc 	terminating = 1;
719433d6423SLionel Sambuc 	virtio_blk_terminate();
720433d6423SLionel Sambuc 
721433d6423SLionel Sambuc 	/* If we get a signal when completely closed, call
722433d6423SLionel Sambuc 	 * exit(). We only leave the blockdriver_mt_task()
723433d6423SLionel Sambuc 	 * loop after completing a request which is not the
724433d6423SLionel Sambuc 	 * case for signals.
725433d6423SLionel Sambuc 	 */
726433d6423SLionel Sambuc 	if (open_count == 0)
727433d6423SLionel Sambuc 		exit(0);
728433d6423SLionel Sambuc }
729433d6423SLionel Sambuc 
730433d6423SLionel Sambuc static void
sef_local_startup(void)731433d6423SLionel Sambuc sef_local_startup(void)
732433d6423SLionel Sambuc {
733433d6423SLionel Sambuc 	sef_setcb_init_fresh(sef_cb_init_fresh);
734433d6423SLionel Sambuc 	sef_setcb_signal_handler(sef_cb_signal_handler);
735433d6423SLionel Sambuc 
7360d6c408fSDavid van Moolenbroek 	/* Enable suppor for live update. */
7370d6c408fSDavid van Moolenbroek 	blockdriver_mt_support_lu();
7380d6c408fSDavid van Moolenbroek 
739433d6423SLionel Sambuc 	sef_startup();
740433d6423SLionel Sambuc }
741433d6423SLionel Sambuc 
742433d6423SLionel Sambuc int
main(int argc,char ** argv)743433d6423SLionel Sambuc main(int argc, char **argv)
744433d6423SLionel Sambuc {
745433d6423SLionel Sambuc 	env_setargs(argc, argv);
746433d6423SLionel Sambuc 	sef_local_startup();
747433d6423SLionel Sambuc 
748433d6423SLionel Sambuc 	blockdriver_mt_task(&virtio_blk_dtab);
749433d6423SLionel Sambuc 
750433d6423SLionel Sambuc 	dprintf(("Terminating"));
751433d6423SLionel Sambuc 	virtio_blk_cleanup();
752433d6423SLionel Sambuc 
753433d6423SLionel Sambuc 	return OK;
754433d6423SLionel Sambuc }
755