# Copyright 2018 syzkaller project authors. All rights reserved.
# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

# AF_VSOCK support.

include <linux/net.h>
include <linux/socket.h>
include <linux/virtio_vsock.h>
include <uapi/linux/fcntl.h>
include <uapi/linux/vhost.h>
include <uapi/linux/vm_sockets.h>
include <uapi/linux/sock_diag.h>
include <uapi/linux/vm_sockets_diag.h>
include <uapi/linux/virtio_net.h>
include <uapi/linux/virtio_config.h>

resource sock_vsock[sock]
resource sock_vsock_stream[sock_vsock]
resource sock_vsock_dgram[sock_vsock]

resource fd_vhost[fd]
resource vhost_vsock[fd_vhost]
resource vhost_net[fd_vhost]

socket$vsock_stream(domain const[AF_VSOCK], type const[SOCK_STREAM], proto const[0]) sock_vsock_stream
# SOCK_SEQPACKET shares bind/connect/accept/listen and the AF_VSOCK sockopts with SOCK_STREAM.
socket$vsock_seqpacket(domain const[AF_VSOCK], type const[SOCK_SEQPACKET], proto const[0]) sock_vsock_stream
bind$vsock_stream(fd sock_vsock_stream, addr ptr[in, sockaddr_vm], addrlen len[addr])
connect$vsock_stream(fd sock_vsock_stream, addr ptr[in, sockaddr_vm], addrlen len[addr])
accept4$vsock_stream(fd sock_vsock_stream, addr ptr[out, sockaddr_vm, opt], addrlen ptr[inout, len[addr, int32]], flags flags[accept_flags]) sock_vsock_stream
setsockopt$SO_VM_SOCKETS_BUFFER_SIZE(fd sock_vsock_stream, level const[AF_VSOCK], opt const[SO_VM_SOCKETS_BUFFER_SIZE], val ptr[in, int64], len len[val])
setsockopt$SO_VM_SOCKETS_BUFFER_MAX_SIZE(fd sock_vsock_stream, level const[AF_VSOCK], opt const[SO_VM_SOCKETS_BUFFER_MAX_SIZE], val ptr[in, int64], len len[val])
setsockopt$SO_VM_SOCKETS_BUFFER_MIN_SIZE(fd sock_vsock_stream, level const[AF_VSOCK], opt const[SO_VM_SOCKETS_BUFFER_MIN_SIZE], val ptr[in, int64], len len[val])
setsockopt$SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD(fd sock_vsock_stream, level const[AF_VSOCK], opt const[SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD], val ptr[in, timeval], len len[val])
setsockopt$SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW(fd sock_vsock_stream, level const[AF_VSOCK], opt const[SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW], val ptr[in, vsock_sock_timeval], len len[val])
# The kernel only requires optlen >= the option size, so 16 bytes fit every option.
getsockopt$vsock(fd sock_vsock_stream, level const[AF_VSOCK], opt flags[vsock_sockopts], val ptr[out, array[int8, 16]], len ptr[inout, len[val, int32]])

# MSG_ZEROCOPY is honored once SO_ZEROCOPY is set; completions are read with recvmsg(MSG_ERRQUEUE).
setsockopt$SO_ZEROCOPY_vsock(fd sock_vsock_stream, level const[SOL_SOCKET], opt const[SO_ZEROCOPY], val ptr[in, bool32], len len[val])
sendmsg$vsock_stream(fd sock_vsock_stream, msg ptr[in, send_msghdr_vsock], f flags[vsock_send_flags])

# vsock_connectible_sendmsg() rejects messages with a destination (-EISCONN/-EOPNOTSUPP) and ignores control data.
send_msghdr_vsock {
	msg_name	const[0, intptr]
	msg_namelen	const[0, int32]
	msg_iov		ptr[in, array[iovec_in]]
	msg_iovlen	len[msg_iov, intptr]
	msg_control	const[0, intptr]
	msg_controllen	const[0, intptr]
	msg_flags	const[0, int32]
}

socket$vsock_dgram(domain const[AF_VSOCK], type const[SOCK_DGRAM], proto const[0]) sock_vsock_dgram
bind$vsock_dgram(fd sock_vsock_dgram, addr ptr[in, sockaddr_vm], addrlen len[addr])
connect$vsock_dgram(fd sock_vsock_dgram, addr ptr[in, sockaddr_vm], addrlen len[addr])

openat$vsock(fd const[AT_FDCWD], file ptr[in, string["/dev/vsock"]], flags flags[open_flags], mode const[0]) fd

openat$vhost_vsock(fd const[AT_FDCWD], file ptr[in, string["/dev/vhost-vsock"]], flags const[O_RDWR], mode const[0]) vhost_vsock
ioctl$VHOST_VSOCK_SET_GUEST_CID(fd vhost_vsock, cmd const[VHOST_VSOCK_SET_GUEST_CID], arg ptr[in, vmaddr_cid64])
ioctl$VHOST_VSOCK_SET_RUNNING(fd vhost_vsock, cmd const[VHOST_VSOCK_SET_RUNNING], arg ptr[in, bool32])

openat$vnet(fd const[AT_FDCWD], file ptr[in, string["/dev/vhost-net"]], flags const[O_RDWR], mode const[0]) vhost_net
write$vhost_msg(fd vhost_net, buf ptr[in, vhost_msg], size len[buf])
write$vhost_msg_v2(fd vhost_net, buf ptr[in, vhost_msg_v2], size len[buf])
ioctl$VHOST_NET_SET_BACKEND(fd vhost_net, cmd const[VHOST_NET_SET_BACKEND], arg ptr[in, vhost_vring_file])

ioctl$VHOST_GET_FEATURES(fd fd_vhost, cmd const[VHOST_GET_FEATURES], arg ptr[out, int64])
ioctl$VHOST_SET_FEATURES(fd fd_vhost, cmd const[VHOST_SET_FEATURES], arg ptr[in, flags[vhost_features, int64]])
ioctl$VHOST_SET_OWNER(fd fd_vhost, cmd const[VHOST_SET_OWNER], arg const[0])
ioctl$VHOST_RESET_OWNER(fd fd_vhost, cmd const[VHOST_RESET_OWNER], arg const[0])
ioctl$VHOST_SET_MEM_TABLE(fd fd_vhost, cmd const[VHOST_SET_MEM_TABLE], arg ptr[in, vhost_memory])
ioctl$VHOST_SET_LOG_BASE(fd fd_vhost, cmd const[VHOST_SET_LOG_BASE], arg ptr[in, ptr64[out, int8]])
ioctl$VHOST_SET_LOG_FD(fd fd_vhost, cmd const[VHOST_SET_LOG_FD], arg ptr[in, fd_event[opt]])
ioctl$VHOST_SET_VRING_NUM(fd fd_vhost, cmd const[VHOST_SET_VRING_NUM], arg ptr[in, vhost_vring_state])
ioctl$VHOST_SET_VRING_BASE(fd fd_vhost, cmd const[VHOST_SET_VRING_BASE], arg ptr[in, vhost_vring_state])
ioctl$VHOST_GET_VRING_BASE(fd fd_vhost, cmd const[VHOST_GET_VRING_BASE], arg ptr[out, vhost_vring_state])
ioctl$VHOST_SET_VRING_ADDR(fd fd_vhost, cmd const[VHOST_SET_VRING_ADDR], arg ptr[in, vhost_vring_addr])
ioctl$VHOST_SET_VRING_KICK(fd fd_vhost, cmd const[VHOST_SET_VRING_KICK], arg ptr[in, vhost_vring_file])
ioctl$VHOST_SET_VRING_CALL(fd fd_vhost, cmd const[VHOST_SET_VRING_CALL], arg ptr[in, vhost_vring_file])
ioctl$VHOST_SET_VRING_ERR(fd fd_vhost, cmd const[VHOST_SET_VRING_ERR], arg ptr[in, vhost_vring_file])
ioctl$VHOST_SET_VRING_ENDIAN(fd fd_vhost, cmd const[VHOST_SET_VRING_ENDIAN], arg ptr[in, vhost_vring_state])
ioctl$VHOST_GET_VRING_ENDIAN(fd fd_vhost, cmd const[VHOST_GET_VRING_ENDIAN], arg ptr[in, vhost_vring_state])
ioctl$VHOST_SET_VRING_BUSYLOOP_TIMEOUT(fd fd_vhost, cmd const[VHOST_SET_VRING_BUSYLOOP_TIMEOUT], arg ptr[in, vhost_vring_state])

ioctl$VHOST_VDPA_GET_DEVICE_ID(fd fd_vhost, cmd const[VHOST_VDPA_GET_DEVICE_ID], arg ptr[out, int32])
ioctl$VHOST_VDPA_GET_STATUS(fd fd_vhost, cmd const[VHOST_VDPA_GET_STATUS], arg ptr[out, int8])
ioctl$VHOST_VDPA_SET_STATUS(fd fd_vhost, cmd const[VHOST_VDPA_SET_STATUS], arg ptr[in, int8])
ioctl$VHOST_VDPA_GET_CONFIG(fd fd_vhost, cmd const[VHOST_VDPA_GET_CONFIG], arg ptr[out, vhost_vdpa_config])
ioctl$VHOST_VDPA_SET_CONFIG(fd fd_vhost, cmd const[VHOST_VDPA_SET_CONFIG], arg ptr[in, vhost_vdpa_config])
ioctl$VHOST_VDPA_SET_VRING_ENABLE(fd fd_vhost, cmd const[VHOST_VDPA_SET_VRING_ENABLE], arg ptr[in, vhost_vring_state])
ioctl$VHOST_VDPA_GET_VRING_NUM(fd fd_vhost, cmd const[VHOST_VDPA_GET_VRING_NUM], arg ptr[out, int16])
ioctl$VHOST_VDPA_SET_CONFIG_CALL(fd fd_vhost, cmd const[VHOST_VDPA_SET_CONFIG_CALL], arg ptr[in, int32])
ioctl$VHOST_VDPA_GET_IOVA_RANGE(fd fd_vhost, cmd const[VHOST_VDPA_GET_IOVA_RANGE], arg ptr[out, vhost_vdpa_iova_range])
ioctl$VHOST_VDPA_GET_CONFIG_SIZE(fd fd_vhost, cmd const[VHOST_VDPA_GET_CONFIG_SIZE], arg ptr[out, int32])
ioctl$VHOST_VDPA_GET_VQS_COUNT(fd fd_vhost, cmd const[VHOST_VDPA_GET_VQS_COUNT], arg ptr[out, int32])
ioctl$VHOST_VDPA_GET_GROUP_NUM(fd fd_vhost, cmd const[VHOST_VDPA_GET_GROUP_NUM], arg ptr[out, int32])
ioctl$VHOST_VDPA_GET_AS_NUM(fd fd_vhost, cmd const[VHOST_VDPA_GET_AS_NUM], arg ptr[out, int32])
ioctl$VHOST_VDPA_GET_VRING_GROUP(fd fd_vhost, cmd const[VHOST_VDPA_GET_VRING_GROUP], arg ptr[inout, vhost_vring_state])
ioctl$VHOST_VDPA_SET_GROUP_ASID(fd fd_vhost, cmd const[VHOST_VDPA_SET_GROUP_ASID], arg ptr[in, vhost_vring_state])

sockaddr_vm {
	svm_family	const[AF_VSOCK, int16]
	svm_reserved1	const[0, int16]
	svm_port	flags[vmaddr_port, int32]
	svm_cid		vmaddr_cid
	svm_flags	flags[vmaddr_flags, int8]
	svm_zero	array[const[0, int8], 3]
}

vmaddr_flags = VMADDR_FLAG_TO_HOST

# tv_usec includes USEC_PER_SEC to reach -ERANGE; tv_sec stays small to avoid long blocking connects.
vsock_sock_timeval {
	tv_sec	int64[0:2]
	tv_usec	int64[0:1000000]
}

vsock_sockopts = SO_VM_SOCKETS_BUFFER_SIZE, SO_VM_SOCKETS_BUFFER_MAX_SIZE, SO_VM_SOCKETS_BUFFER_MIN_SIZE, SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD, SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW
vsock_send_flags = MSG_DONTWAIT, MSG_EOR, MSG_NOSIGNAL, MSG_ZEROCOPY

# struct vsock_diag_req is 24 bytes; the generic 2-byte sock_diag_req is rejected by vsock_diag_handler_dump().
sendmsg$vsock_diag(fd sock_diag, msg ptr[in, msghdr_netlink[netlink_msg[SOCK_DIAG_BY_FAMILY, vsock_diag_req, void]]], f flags[send_flags])

vsock_diag_req {
	sdiag_family	const[AF_VSOCK, int8]
	sdiag_protocol	const[0, int8]
	pad		const[0, int16]
	vdiag_states	int32
	vdiag_ino	const[0, int32]
	vdiag_show	const[0, int32]
	vdiag_cookie	array[const[0, int32], 2]
}

vmaddr_cid [
	any	const[VMADDR_CID_ANY, int32]
	hyper	const[VMADDR_CID_HYPERVISOR, int32]
	local	const[VMADDR_CID_LOCAL, int32]
	host	const[VMADDR_CID_HOST, int32]
	my	proc[1234, 2, int32]
]

vmaddr_cid64 {
	cid	vmaddr_cid
	pad	const[0, int32]
} [align[8]]

vhost_memory {
	nregions	len[regions, int32]
	padding		const[0, int32]
	regions		array[vhost_memory_region]
}

vhost_memory_region {
	guest_phys_addr	flags[kvm_guest_addrs, int64]
	memory_size	bytesize[userspace_addr, int64]
	userspace_addr	ptr64[out, array[int8]]
	flags_padding	const[0, int64]
}

vhost_vring_state {
	index	flags[vhost_vring_index, int32]
	num	int32
}

vhost_vring_addr {
	index		flags[vhost_vring_index, int32]
	flags		int32[0:1]
	desc_user_addr	ptr64[out, array[int8]]
	used_user_addr	ptr64[out, array[int8]]
	avail_user_addr	ptr64[out, array[int8]]
	log_guest_addr	flags[kvm_guest_addrs, int64]
}

vhost_vring_file {
	index	flags[vhost_vring_index, int32]
	fd	fd_event[opt]
}

vhost_msg {
	type	const[VHOST_IOTLB_MSG, int32]
	iotlb	vhost_iotlb_msg
} [size[VHOST_MSG_SIZE]]

define VHOST_MSG_SIZE	sizeof(struct vhost_msg)

vhost_msg_v2 {
	type		const[VHOST_IOTLB_MSG_V2, int32]
	reserved	const[0, int32]
	iotlb		vhost_iotlb_msg
} [size[VHOST_MSG_V2_SIZE]]

vhost_vdpa_config {
	off	int32
	len	len[buf, int32]
	buf	array[int8]
}

vhost_vdpa_iova_range {
	first	int64
	last	int64
}

define VHOST_MSG_V2_SIZE	sizeof(struct vhost_msg_v2)

vhost_iotlb_msg {
# TODO: is it host or guest address?
	iova	ptr64[out, array[int8]]
	size	len[iova, int64]
	uaddr	ptr64[out, array[int8]]
	perm	flags[vhost_iotlb_perm, int8]
	type	flags[vhost_iotlb_type, int8]
}

vmaddr_port = VMADDR_PORT_ANY, 0, 10000, 10001
vhost_features = VHOST_LOG_ALL, VIRTIO_NOTIFY_ON_EMPTY, VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, VIRTIO_ANY_LAYOUT, VIRTIO_VERSION_1, VHOST_NET_VIRTIO_NET_HDR, VIRTIO_NET_MRG_RXBUF, VIRTIO_IOMMU_PLATFORM
vhost_vring_index = VSOCK_VQ_RX, VSOCK_VQ_TX, VSOCK_VQ_EVENT, VSOCK_VQ_MAX
vhost_iotlb_type = VHOST_IOTLB_MISS, VHOST_IOTLB_UPDATE, VHOST_IOTLB_INVALIDATE, VHOST_IOTLB_ACCESS_FAIL
vhost_iotlb_perm = VHOST_ACCESS_RO, VHOST_ACCESS_WO, VHOST_ACCESS_RW

# This list contains guest addresses from executor/kvm.h, page boundaries,
# and unaligned page-crossing offsets (e.g. 0xffc, 0x1ff8) to exercise instruction
# split-page emulation, unaligned MMIO, and cross-boundary ioeventfd handlers.
kvm_guest_addrs = 0, 1, 2, 4, 0xffc, 0xffe, 0x1000, 0x1ff8, 0x1ffc, 0x1ffe, 0x2000, 0x2ffc, 0x3000, 0x3ff8, 0x4000, 0x5000, 0x6000, 0x7000, 0x9000, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, 0xf000, 0x10000, 0x25000, 0x26000, 0x30000, 0x40000, 0x41000, 0x50000, 0x54000, 0x58000, 0x60000, 0x70000, 0x100000, 0x200000, 0x8000000, 0x8080000, 0x80a0000, 0xdddd0000, 0xdddd1000, 0xeeee0000, 0xeeee8000, 0xeeef0000, 0xfec00000, 0xffff1000, 0xffffffff

define VHOST_LOG_ALL	1 << VHOST_F_LOG_ALL
define VIRTIO_NOTIFY_ON_EMPTY	1 << VIRTIO_F_NOTIFY_ON_EMPTY
define VIRTIO_RING_F_INDIRECT_DESC	1 << VIRTIO_RING_F_INDIRECT_DESC
define VIRTIO_RING_F_EVENT_IDX	1 << VIRTIO_RING_F_EVENT_IDX
define VIRTIO_ANY_LAYOUT	1 << VIRTIO_F_ANY_LAYOUT
define VIRTIO_VERSION_1	1 << VIRTIO_F_VERSION_1
define VHOST_NET_VIRTIO_NET_HDR	1 << VHOST_NET_F_VIRTIO_NET_HDR
define VIRTIO_NET_MRG_RXBUF	1 << VIRTIO_NET_F_MRG_RXBUF
define VIRTIO_IOMMU_PLATFORM	1 << VIRTIO_F_ACCESS_PLATFORM
