xref: /openbsd-src/usr.sbin/vmd/virtio.h (revision 7ccb23ddbdd55de383a971a182d990cf044f966f)
1*7ccb23ddSdv /*	$OpenBSD: virtio.h,v 1.53 2025/01/08 15:46:10 dv Exp $	*/
215caf263Sreyk 
3f3c0184aSmlarkin /*
4f3c0184aSmlarkin  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
5f3c0184aSmlarkin  *
6f3c0184aSmlarkin  * Permission to use, copy, modify, and distribute this software for any
7f3c0184aSmlarkin  * purpose with or without fee is hereby granted, provided that the above
8f3c0184aSmlarkin  * copyright notice and this permission notice appear in all copies.
9f3c0184aSmlarkin  *
10f3c0184aSmlarkin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11f3c0184aSmlarkin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12f3c0184aSmlarkin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13f3c0184aSmlarkin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14f3c0184aSmlarkin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15f3c0184aSmlarkin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16f3c0184aSmlarkin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17f3c0184aSmlarkin  */
18f3c0184aSmlarkin 
196eb4c859Sdv #include <sys/types.h>
206eb4c859Sdv 
21d7f9c71cSmlarkin #include <dev/pv/virtioreg.h>
226c31e103Sdv #include <net/if_tun.h>
23f3c0184aSmlarkin 
246eb4c859Sdv #include <event.h>
256eb4c859Sdv 
266eb4c859Sdv #include "vmd.h"
276eb4c859Sdv 
286eb4c859Sdv #ifndef _VIRTIO_H_
296eb4c859Sdv #define _VIRTIO_H_
306eb4c859Sdv 
31f3c0184aSmlarkin #define VIRTQUEUE_ALIGN(n)	(((n)+(VIRTIO_PAGE_SIZE-1))&    \
32f3c0184aSmlarkin 				    ~(VIRTIO_PAGE_SIZE-1))
3362df93eeSreyk #define ALIGNSZ(sz, align)	((sz + align - 1) & ~(align - 1))
3462df93eeSreyk #define MIN(a,b)		(((a)<(b))?(a):(b))
35f3c0184aSmlarkin 
3620e554f8Sdv /* Queue sizes must be power of two and less than IOV_MAX (1024). */
37f3c0184aSmlarkin #define VIORND_QUEUE_SIZE	64
38f3c0184aSmlarkin #define VIORND_QUEUE_MASK	(VIORND_QUEUE_SIZE - 1)
39f3c0184aSmlarkin 
40f4229d5cSmlarkin #define VIOBLK_QUEUE_SIZE	128
41f3c0184aSmlarkin #define VIOBLK_QUEUE_MASK	(VIOBLK_QUEUE_SIZE - 1)
4220e554f8Sdv #define VIOBLK_SEG_MAX		(VIOBLK_QUEUE_SIZE - 2)
43f3c0184aSmlarkin 
4495ab188fSccardenas #define VIOSCSI_QUEUE_SIZE	128
4595ab188fSccardenas #define VIOSCSI_QUEUE_MASK	(VIOSCSI_QUEUE_SIZE - 1)
4695ab188fSccardenas 
479e230518Smlarkin #define VIONET_QUEUE_SIZE	256
48f3c0184aSmlarkin #define VIONET_QUEUE_MASK	(VIONET_QUEUE_SIZE - 1)
49f3c0184aSmlarkin 
506c31e103Sdv /* Virtio network device is backed by tap(4), so inherit limits */
516c31e103Sdv #define VIONET_HARD_MTU		TUNMRU
526c31e103Sdv #define VIONET_MIN_TXLEN	ETHER_HDR_LEN
536c31e103Sdv #define VIONET_MAX_TXLEN	VIONET_HARD_MTU + ETHER_HDR_LEN
546c31e103Sdv 
553320a88dSreyk /* VMM Control Interface shutdown timeout (in seconds) */
56*7ccb23ddSdv #define VMMCI_TIMEOUT_SHORT	3
57*7ccb23ddSdv #define VMMCI_TIMEOUT_LONG	120
58f84d5d33Sreyk 
5995ab188fSccardenas /* All the devices we support have either 1, 2 or 3 queues */
6095ab188fSccardenas /* viornd - 1 queue
6195ab188fSccardenas  * vioblk - 1 queue
6295ab188fSccardenas  * vionet - 2 queues
6395ab188fSccardenas  * vioscsi - 3 queues
6495ab188fSccardenas  */
6595ab188fSccardenas #define VIRTIO_MAX_QUEUES	3
66f3c0184aSmlarkin 
673481ecdfSdv #define MAXPHYS	(64 * 1024)	/* max raw I/O transfer size */
683481ecdfSdv 
69e44e0427Sstefan /*
700bd10b9fSdv  * Rename the address config register to be more descriptive.
710bd10b9fSdv  */
720bd10b9fSdv #define VIRTIO_CONFIG_QUEUE_PFN	VIRTIO_CONFIG_QUEUE_ADDRESS
7320e554f8Sdv #define DEVICE_NEEDS_RESET	VIRTIO_CONFIG_DEVICE_STATUS_DEVICE_NEEDS_RESET
7420e554f8Sdv #define DESC_WRITABLE(/* struct vring_desc */ x)	\
7520e554f8Sdv 	(((x)->flags & VRING_DESC_F_WRITE) ? 1 : 0)
7620e554f8Sdv 
770bd10b9fSdv 
780bd10b9fSdv /*
793481ecdfSdv  * VM <-> Device messaging.
803481ecdfSdv  */
813481ecdfSdv struct viodev_msg {
823481ecdfSdv 	uint8_t type;
833481ecdfSdv #define VIODEV_MSG_INVALID	0
843481ecdfSdv #define VIODEV_MSG_READY	1
853481ecdfSdv #define VIODEV_MSG_ERROR	2
863481ecdfSdv #define VIODEV_MSG_KICK		3
873481ecdfSdv #define VIODEV_MSG_IO_READ	4
883481ecdfSdv #define VIODEV_MSG_IO_WRITE	5
893481ecdfSdv #define VIODEV_MSG_DUMP		6
903481ecdfSdv #define VIODEV_MSG_SHUTDOWN	7
913481ecdfSdv 
923481ecdfSdv 	uint16_t reg;		/* VirtIO register */
933481ecdfSdv 	uint8_t io_sz;		/* IO instruction size */
943481ecdfSdv 	uint8_t vcpu;		/* VCPU id */
953481ecdfSdv 	uint8_t irq;		/* IRQ number */
963481ecdfSdv 
973481ecdfSdv 	int8_t state;		/* Interrupt state toggle (if any) */
983481ecdfSdv #define INTR_STATE_ASSERT	 1
993481ecdfSdv #define INTR_STATE_NOOP		 0
1003481ecdfSdv #define INTR_STATE_DEASSERT	-1
1013481ecdfSdv 
1023481ecdfSdv 	uint32_t data;		/* Data (if any) */
1033481ecdfSdv 	uint8_t data_valid;	/* 1 if data field is populated. */
1043481ecdfSdv } __packed;
1053481ecdfSdv 
1063481ecdfSdv /*
107e44e0427Sstefan  * This struct stores notifications from a virtio driver. There is
108e44e0427Sstefan  * one such struct per virtio device.
109e44e0427Sstefan  */
110f3c0184aSmlarkin struct virtio_io_cfg {
111f3c0184aSmlarkin 	uint32_t device_feature;
112f3c0184aSmlarkin 	uint32_t guest_feature;
1130bd10b9fSdv 	uint32_t queue_pfn;
114f3c0184aSmlarkin 	uint16_t queue_size;
115f3c0184aSmlarkin 	uint16_t queue_select;
116f3c0184aSmlarkin 	uint16_t queue_notify;
117f3c0184aSmlarkin 	uint8_t device_status;
118f3c0184aSmlarkin 	uint8_t isr_status;
119f3c0184aSmlarkin };
120f3c0184aSmlarkin 
1219617633bSccardenas struct virtio_backing {
1229617633bSccardenas 	void  *p;
12320e554f8Sdv 	ssize_t (*pread)(void *, char *, size_t, off_t);
12420e554f8Sdv 	ssize_t (*preadv)(void *, struct iovec *, int, off_t);
12520e554f8Sdv 	ssize_t (*pwrite)(void *, char *, size_t, off_t);
12620e554f8Sdv 	ssize_t (*pwritev)(void *, struct iovec *, int, off_t);
12720e554f8Sdv 	void (*close)(void *, int);
1289617633bSccardenas };
1299617633bSccardenas 
130e44e0427Sstefan /*
131e44e0427Sstefan  * A virtio device can have several virtqs. For example, vionet has one virtq
132e44e0427Sstefan  * each for transmitting and receiving packets. This struct describes the state
133e44e0427Sstefan  * of one virtq, such as their address in memory, size, offsets of rings, etc.
134e44e0427Sstefan  * There is one virtio_vq_info per virtq.
135e44e0427Sstefan  */
136f3c0184aSmlarkin struct virtio_vq_info {
137e44e0427Sstefan 	/* Guest physical address of virtq */
1380bd10b9fSdv 	uint64_t q_gpa;
1390bd10b9fSdv 
1400bd10b9fSdv 	/* Host virtual address of virtq */
1410bd10b9fSdv 	void *q_hva;
142e44e0427Sstefan 
143e44e0427Sstefan 	/* Queue size: number of queue entries in virtq */
144f3c0184aSmlarkin 	uint32_t qs;
145e44e0427Sstefan 
146e44e0427Sstefan 	/*
147e44e0427Sstefan 	 * The offset of the 'available' ring within the virtq located at
148e44e0427Sstefan 	 * guest physical address qa above
149e44e0427Sstefan 	 */
150f3c0184aSmlarkin 	uint32_t vq_availoffset;
151e44e0427Sstefan 
152e44e0427Sstefan 	/*
153e44e0427Sstefan 	 * The offset of the 'used' ring within the virtq located at guest
154e44e0427Sstefan 	 * physical address qa above
155e44e0427Sstefan 	 */
156f3c0184aSmlarkin 	uint32_t vq_usedoffset;
157e44e0427Sstefan 
158e44e0427Sstefan 	/*
159e44e0427Sstefan 	 * The index into a slot of the 'available' ring that a virtio device
160e44e0427Sstefan 	 * can consume next
161e44e0427Sstefan 	 */
162f3c0184aSmlarkin 	uint16_t last_avail;
163e44e0427Sstefan 
164e44e0427Sstefan 	/*
165e44e0427Sstefan 	 * The most recent index into the 'available' ring that a virtio
166e44e0427Sstefan 	 * driver notified to the host.
167e44e0427Sstefan 	 */
168e44e0427Sstefan 	uint16_t notified_avail;
169f3c0184aSmlarkin };
170f3c0184aSmlarkin 
17128705897Sccardenas /*
17228705897Sccardenas  * Each virtio driver has a notifyq method where one or more messages
17328705897Sccardenas  * are ready to be processed on a given virtq.  As such, various
17428705897Sccardenas  * pieces of information are needed to provide ring accounting while
17528705897Sccardenas  * processing a given message such as virtq indexes, vring pointers, and
17628705897Sccardenas  * vring descriptors.
17728705897Sccardenas  */
17828705897Sccardenas struct virtio_vq_acct {
17928705897Sccardenas 
18028705897Sccardenas 	/* index of previous avail vring message */
18128705897Sccardenas 	uint16_t idx;
18228705897Sccardenas 
18328705897Sccardenas 	/* index of current message containing the request */
18428705897Sccardenas 	uint16_t req_idx;
18528705897Sccardenas 
18628705897Sccardenas 	/* index of current message containing the response */
18728705897Sccardenas 	uint16_t resp_idx;
18828705897Sccardenas 
18928705897Sccardenas 	/* vring descriptor pointer */
19028705897Sccardenas 	struct vring_desc *desc;
19128705897Sccardenas 
19228705897Sccardenas 	/* vring descriptor pointer for request header and data */
19328705897Sccardenas 	struct vring_desc *req_desc;
19428705897Sccardenas 
19528705897Sccardenas 	/* vring descriptor pointer for response header and data */
19628705897Sccardenas 	struct vring_desc *resp_desc;
19728705897Sccardenas 
19828705897Sccardenas 	/* pointer to the available vring */
19928705897Sccardenas 	struct vring_avail *avail;
20028705897Sccardenas 
20128705897Sccardenas 	/* pointer to the used vring */
20228705897Sccardenas 	struct vring_used *used;
20328705897Sccardenas };
20428705897Sccardenas 
205f3c0184aSmlarkin struct viornd_dev {
206f3c0184aSmlarkin 	struct virtio_io_cfg cfg;
207f3c0184aSmlarkin 
208f3c0184aSmlarkin 	struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
209813e3047Spd 
210813e3047Spd 	uint8_t pci_id;
2117b7c4b51Smlarkin 	int irq;
2127b7c4b51Smlarkin 	uint32_t vm_id;
213f3c0184aSmlarkin };
214f3c0184aSmlarkin 
215f3c0184aSmlarkin struct vioblk_dev {
216f3c0184aSmlarkin 	struct virtio_io_cfg cfg;
217f3c0184aSmlarkin 	struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
2189617633bSccardenas 	struct virtio_backing file;
219f3c0184aSmlarkin 
2203481ecdfSdv 	int disk_fd[VM_MAX_BASE_PER_DISK];	/* fds for disk image(s) */
2213481ecdfSdv 	uint8_t ndisk_fd;	/* number of valid disk fds */
22220e554f8Sdv 	uint64_t capacity;	/* size in 512 byte sectors */
22320e554f8Sdv 	uint32_t seg_max;	/* maximum number of segments */
224813e3047Spd 
2253481ecdfSdv 	unsigned int idx;
226f3c0184aSmlarkin };
227f3c0184aSmlarkin 
22895ab188fSccardenas /* vioscsi will use at least 3 queues - 5.6.2 Virtqueues
22995ab188fSccardenas  * Current implementation will use 3
23095ab188fSccardenas  * 0 - control
23195ab188fSccardenas  * 1 - event
23295ab188fSccardenas  * 2 - requests
23395ab188fSccardenas  */
23495ab188fSccardenas struct vioscsi_dev {
23595ab188fSccardenas 	struct virtio_io_cfg cfg;
23695ab188fSccardenas 
23795ab188fSccardenas 	struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
23895ab188fSccardenas 
2399617633bSccardenas 	struct virtio_backing file;
2409617633bSccardenas 
24195ab188fSccardenas 	/* is the device locked */
24295ab188fSccardenas 	int locked;
24395ab188fSccardenas 	/* size of iso file in bytes */
24495ab188fSccardenas 	uint64_t sz;
24595ab188fSccardenas 	/* last block address read */
24695ab188fSccardenas 	uint64_t lba;
24795ab188fSccardenas 	/* number of blocks represented in iso */
24895ab188fSccardenas 	uint64_t n_blocks;
24995ab188fSccardenas 	uint32_t max_xfer;
25095ab188fSccardenas 
25195ab188fSccardenas 	uint8_t pci_id;
2527b7c4b51Smlarkin 	uint32_t vm_id;
2537b7c4b51Smlarkin 	int irq;
25495ab188fSccardenas };
25595ab188fSccardenas 
256f3c0184aSmlarkin struct vionet_dev {
257f3c0184aSmlarkin 	struct virtio_io_cfg cfg;
258f3c0184aSmlarkin 	struct virtio_vq_info vq[VIRTIO_MAX_QUEUES];
259f3c0184aSmlarkin 
2603481ecdfSdv 	int data_fd;		/* fd for our tap device */
2613481ecdfSdv 
262f3c0184aSmlarkin 	uint8_t mac[6];
26397f33f1dSdv 	uint8_t hostmac[6];
2642b2a5f0dSreyk 	int lockedmac;
265470adcf5Sreyk 	int local;
266cc104512Sclaudio 	int pxeboot;
2672272e586Sdv 	struct local_prefix local_prefix;
268813e3047Spd 
2693481ecdfSdv 	unsigned int idx;
2703481ecdfSdv };
2713481ecdfSdv 
2723481ecdfSdv struct virtio_dev {
2733481ecdfSdv 	union {
2743481ecdfSdv 		struct vioblk_dev vioblk;
2753481ecdfSdv 		struct vionet_dev vionet;
2763481ecdfSdv 	};
2773481ecdfSdv 
2783481ecdfSdv 	struct imsgev async_iev;
2793481ecdfSdv 	struct imsgev sync_iev;
2803481ecdfSdv 
2813481ecdfSdv 	int sync_fd;		/* fd for synchronous channel */
2823481ecdfSdv 	int async_fd;		/* fd for async channel */
2833481ecdfSdv 
284813e3047Spd 	uint8_t pci_id;
2853481ecdfSdv 	uint32_t vm_id;
2863481ecdfSdv 	uint32_t vm_vmid;
2873481ecdfSdv 	int irq;
2883481ecdfSdv 
2893481ecdfSdv 	pid_t dev_pid;
2903481ecdfSdv 	char dev_type;
2913481ecdfSdv 	SLIST_ENTRY(virtio_dev) dev_next;
292f3c0184aSmlarkin };
293f3c0184aSmlarkin 
294f3c0184aSmlarkin struct virtio_net_hdr {
295f3c0184aSmlarkin 	uint8_t flags;
296f3c0184aSmlarkin 	uint8_t gso_type;
297f3c0184aSmlarkin 	uint16_t hdr_len;
298f3c0184aSmlarkin 	uint16_t gso_size;
299f3c0184aSmlarkin 	uint16_t csum_start;
300f3c0184aSmlarkin 	uint16_t csum_offset;
301f3c0184aSmlarkin 
302f3c0184aSmlarkin 	/*
303f3c0184aSmlarkin 	 * num_buffers is only used if VIRTIO_NET_F_MRG_RXBUF is negotiated.
304f3c0184aSmlarkin 	 * vmd(8) doesn't negotiate that, but the field is listed here
305f3c0184aSmlarkin 	 * for completeness sake.
306f3c0184aSmlarkin 	 */
307f3c0184aSmlarkin /*	uint16_t num_buffers; */
308f3c0184aSmlarkin };
309f3c0184aSmlarkin 
310f84d5d33Sreyk enum vmmci_cmd {
311f84d5d33Sreyk 	VMMCI_NONE = 0,
312f84d5d33Sreyk 	VMMCI_SHUTDOWN,
313f84d5d33Sreyk 	VMMCI_REBOOT,
314e82d5294Smlarkin 	VMMCI_SYNCRTC,
315f84d5d33Sreyk };
316f84d5d33Sreyk 
317f84d5d33Sreyk struct vmmci_dev {
318f84d5d33Sreyk 	struct virtio_io_cfg cfg;
3193320a88dSreyk 	struct event timeout;
320981cad08Sreyk 	struct timeval time;
321f84d5d33Sreyk 	enum vmmci_cmd cmd;
322f84d5d33Sreyk 	uint32_t vm_id;
323f84d5d33Sreyk 	int irq;
324813e3047Spd 	uint8_t pci_id;
325*7ccb23ddSdv 
326*7ccb23ddSdv 	pthread_mutex_t mutex;
327*7ccb23ddSdv 	struct vm_dev_pipe dev_pipe;
328f84d5d33Sreyk };
329f3c0184aSmlarkin 
33020e554f8Sdv /* XXX to be removed once vioscsi is adapted to vectorized io. */
33195ab188fSccardenas struct ioinfo {
33295ab188fSccardenas 	uint8_t *buf;
33395ab188fSccardenas 	ssize_t len;
33495ab188fSccardenas 	off_t offset;
33595ab188fSccardenas };
33695ab188fSccardenas 
337470adcf5Sreyk /* virtio.c */
33873613953Sreyk void virtio_init(struct vmd_vm *, int, int[][VM_MAX_BASE_PER_DISK], int *);
33908d0da61Sdv void virtio_broadcast_imsg(struct vmd_vm *, uint16_t, void *, uint16_t);
34073a98491Sdv void virtio_stop(struct vmd_vm *);
34173a98491Sdv void virtio_start(struct vmd_vm *);
34250bebf2cSccardenas void virtio_shutdown(struct vmd_vm *);
343149417b6Sreyk int virtio_dump(int);
34473a98491Sdv int virtio_restore(int, struct vmd_vm *, int, int[][VM_MAX_BASE_PER_DISK],
34573a98491Sdv     int *);
3463481ecdfSdv const char *virtio_reg_name(uint8_t);
347f3c0184aSmlarkin uint32_t vring_size(uint32_t);
348a246f7a0Sdv int vm_device_pipe(struct virtio_dev *, void (*)(int, short, void *),
349a246f7a0Sdv     struct event_base *);
3503481ecdfSdv int virtio_pci_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
351c4fd4c5bSdv void virtio_assert_irq(struct virtio_dev *, int);
352c4fd4c5bSdv void virtio_deassert_irq(struct virtio_dev *, int);
353f3c0184aSmlarkin 
354eef1411cSmlarkin int virtio_rnd_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
355149417b6Sreyk int viornd_dump(int);
35673a98491Sdv int viornd_restore(int, struct vmd_vm *);
357f3c0184aSmlarkin void viornd_update_qs(void);
358f3c0184aSmlarkin void viornd_update_qa(void);
359f3c0184aSmlarkin int viornd_notifyq(void);
360f3c0184aSmlarkin 
3614d2a1fb2Sreyk ssize_t virtio_qcow2_get_base(int, char *, size_t, const char *);
362ead1b146Sdv int virtio_qcow2_create(const char *, const char *, uint64_t);
36362df93eeSreyk int virtio_qcow2_init(struct virtio_backing *, off_t *, int*, size_t);
364ead1b146Sdv int virtio_raw_create(const char *, uint64_t);
36562df93eeSreyk int virtio_raw_init(struct virtio_backing *, off_t *, int*, size_t);
3669617633bSccardenas 
367149417b6Sreyk int vioblk_dump(int);
36873a98491Sdv int vioblk_restore(int, struct vmd_vm *, int[][VM_MAX_BASE_PER_DISK]);
369f3c0184aSmlarkin 
370149417b6Sreyk int vionet_dump(int);
371149417b6Sreyk int vionet_restore(int, struct vmd_vm *, int *);
37297f33f1dSdv void vionet_set_hostmac(struct vmd_vm *, unsigned int, uint8_t *);
373f3c0184aSmlarkin 
374eef1411cSmlarkin int vmmci_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
375149417b6Sreyk int vmmci_dump(int);
376149417b6Sreyk int vmmci_restore(int, uint32_t);
377f84d5d33Sreyk int vmmci_ctl(unsigned int);
3783320a88dSreyk void vmmci_ack(unsigned int);
3793320a88dSreyk void vmmci_timeout(int, short, void *);
380f84d5d33Sreyk 
381f3c0184aSmlarkin const char *vioblk_cmd_name(uint32_t);
38295ab188fSccardenas int vioscsi_dump(int);
38373a98491Sdv int vioscsi_restore(int, struct vmd_vm *, int);
384470adcf5Sreyk 
385470adcf5Sreyk /* dhcp.c */
3863481ecdfSdv ssize_t dhcp_request(struct virtio_dev *, char *, size_t, char **);
38795ab188fSccardenas 
38895ab188fSccardenas /* vioscsi.c */
38995ab188fSccardenas int vioscsi_io(int, uint16_t, uint32_t *, uint8_t *, void *, uint8_t);
39095ab188fSccardenas void vioscsi_update_qs(struct vioscsi_dev *);
39195ab188fSccardenas void vioscsi_update_qa(struct vioscsi_dev *);
39295ab188fSccardenas int vioscsi_notifyq(struct vioscsi_dev *);
3936eb4c859Sdv 
3946eb4c859Sdv #endif /* _VIRTIO_H_ */
395