# 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.

include <linux/nvme-tcp.h>
include <linux/sizes.h>
include <linux/nvme.h>
include <linux/socket.h>
include <uapi/linux/in.h>
include <linux/net.h>

#########################################
### Generic NVMe over TCP ###

nvme_of_msg {
	pdu	nvme_tcp_pdu
	payload	optional[buffer[in]]
} [packed]

nvme_tcp_pdu [
	icreq			nvme_tcp_icreq_pdu
	icresp			nvme_tcp_icresp_pdu
	cmd			nvme_tcp_cmd_pdu
	rsp			nvme_tcp_rsp_pdu
	r2t			nvme_tcp_r2t_pdu
	data_h2c		nvme_tcp_data_pdu_h2c
	data_h2c_hdigest	nvme_tcp_data_pdu_h2c_hdigest
	data_h2c_no_hdigest	nvme_tcp_data_pdu_h2c_no_hdigest
	data_c2h		nvme_tcp_data_pdu_c2h
]

type nvme_tcp_hdr_t_const[PDU_TYPE, HLEN_SIZE] {
	type	const[PDU_TYPE, int8]
	flags	flags[nvme_tcp_pdu_flags, int8]
	hlen	const[HLEN_SIZE, int8]
	pdo	int8
	plen	int32
} [packed]

type nvme_tcp_hdr_t_const_pdo[PDU_TYPE, HLEN_SIZE, PDO_SIZE] {
	type	const[PDU_TYPE, int8]
	flags	flags[nvme_tcp_pdu_flags, int8]
	hlen	const[HLEN_SIZE, int8]
	pdo	const[PDO_SIZE, int8]
	plen	int32
} [packed]

type nvme_tcp_hdr_t_const_plen[PDU_TYPE, HLEN_SIZE, PLEN_SIZE] {
	type	const[PDU_TYPE, int8]
	flags	flags[nvme_tcp_pdu_flags, int8]
	hlen	const[HLEN_SIZE, int8]
	pdo	int8
	plen	const[PLEN_SIZE, int32]
} [packed]

nvme_tcp_pdu_flags = NVME_TCP_F_HDGST, NVME_TCP_F_DDGST, NVME_TCP_F_DATA_LAST, NVME_TCP_F_DATA_SUCCESS

nvme_tcp_digest_option = NVME_TCP_HDR_DIGEST_ENABLE, NVME_TCP_DATA_DIGEST_ENABLE

nvme_tcp_fatal_error_status = 0, NVME_TCP_FES_INVALID_PDU_HDR, NVME_TCP_FES_PDU_SEQ_ERR, NVME_TCP_FES_HDR_DIGEST_ERR, NVME_TCP_FES_DATA_OUT_OF_RANGE, NVME_TCP_FES_R2T_LIMIT_EXCEEDED, NVME_TCP_FES_DATA_LIMIT_EXCEEDED, NVME_TCP_FES_UNSUPPORTED_PARAM

nvme_tcp_pfv = NVME_TCP_PFV_1_0

define NVME_TCP_DATA_PDU_SIZE	sizeof(struct nvme_tcp_data_pdu)
define NVME_TCP_ICREQ_PDU_SIZE	sizeof(struct nvme_tcp_icreq_pdu)
define NVME_TCP_CMD_PDU_SIZE	sizeof(struct nvme_tcp_cmd_pdu)
define NVME_TCP_ICRESP_PDU_SIZE	sizeof(struct nvme_tcp_icresp_pdu)

define NVME_TCP_DISC_PORT	8009
define NVME_TCP_ADMIN_CCSZ	SZ_8K
define NVME_TCP_DIGEST_LENGTH	4
define NVME_TCP_MIN_MAXH2CDATA	4096

### End of generic ###
#########################################

#########################################
### nvme_tcp_data_pdu ###

type nvme_tcp_data_pdu[HDR_TYPE] {
	hdr		HDR_TYPE
	command_id	int16
	ttag		int16
	data_offset	int32
	data_length	int32
	rsvd		array[int8, 4]
} [size[NVME_TCP_DATA_PDU_SIZE]]

type nvme_tcp_hdr_data_pdu_h2c nvme_tcp_hdr_t_const[nvme_tcp_h2c_data, NVME_TCP_DATA_PDU_SIZE]
type nvme_tcp_data_pdu_h2c nvme_tcp_data_pdu[nvme_tcp_hdr_data_pdu_h2c]

type nvme_tcp_hdr_data_pdu_h2c_hdigest nvme_tcp_hdr_t_const_pdo[nvme_tcp_h2c_data, NVME_TCP_DATA_PDU_SIZE, NVME_TCP_HDR_H2C_DIGEST_PDO_HDIGEST]
type nvme_tcp_data_pdu_h2c_hdigest nvme_tcp_data_pdu[nvme_tcp_hdr_data_pdu_h2c_hdigest]

type nvme_tcp_hdr_data_pdu_h2c_no_hdigest nvme_tcp_hdr_t_const_pdo[nvme_tcp_h2c_data, NVME_TCP_DATA_PDU_SIZE, NVME_TCP_HDR_H2C_DIGEST_PDO_NO_HDIGEST]
type nvme_tcp_data_pdu_h2c_no_hdigest nvme_tcp_data_pdu[nvme_tcp_hdr_data_pdu_h2c_no_hdigest]

type nvme_tcp_hdr_data_pdu_c2h nvme_tcp_hdr_t_const[nvme_tcp_c2h_data, NVME_TCP_DATA_PDU_SIZE]
type nvme_tcp_data_pdu_c2h nvme_tcp_data_pdu[nvme_tcp_hdr_data_pdu_c2h]

define NVME_TCP_HDR_H2C_DIGEST_PDO_HDIGEST	NVME_TCP_DATA_PDU_SIZE + NVME_TCP_DIGEST_LENGTH
define NVME_TCP_HDR_H2C_DIGEST_PDO_NO_HDIGEST	NVME_TCP_DATA_PDU_SIZE
define NVME_TCP_HDR_H2C_DIGEST_PLEN_HDIGEST_DDIGEST	NVME_TCP_HDR_H2C_DIGEST_PDO_HDIGEST + NVME_TCP_DIGEST_LENGTH

### End of nvme_tcp_r2t_pdu ###
#########################################

#########################################
### nvme_tcp_r2t_pdu ###

nvme_tcp_r2t_pdu {
	hdr		nvme_tcp_hdr_r2t_pdu
	command_id	int16
	ttag		int16
	r2t_offset	int32
	r2t_length	int32
	rsvd		array[const[0, int8], 4]
} [size[NVME_TCP_DATA_PDU_SIZE]]

type nvme_tcp_hdr_r2t_pdu nvme_tcp_hdr_t_const[nvme_tcp_r2t, NVME_TCP_DATA_PDU_SIZE]

### End of nvme_tcp_r2t_pdu ###
#########################################

#########################################
### nvme_tcp_rsp_pdu ###

nvme_tcp_rsp_pdu {
	hdr	nvme_tcp_hdr_rsp_pdu
	cqe	nvme_completion
} [size[NVME_TCP_DATA_PDU_SIZE]]

nvme_completion {
	result		nvme_result
	sq_head		int16
	sq_id		int16
	command_id	int16
	status		flags[nvme_tcp_fatal_error_status, int16]
} [packed]

nvme_result [
	u16	int16
	u32	int32
	u64	int64
]

type nvme_tcp_hdr_rsp_pdu nvme_tcp_hdr_t_const[nvme_tcp_rsp, NVME_TCP_DATA_PDU_SIZE]

### End of nvme_tcp_rsp_pdu ###
#########################################

#########################################
### nvme_tcp_icresp_pdu ###

nvme_tcp_icresp_pdu {
	hdr	nvme_tcp_hdr_icresp_pdu
	pfv	flags[nvme_tcp_pfv, int16]
# Linux NVMe driver only support cpda = 0
	cpda	const[0, int8]
	digest	flags[nvme_tcp_digest_option, int8]
	maxdata	int32
	rsvd	array[const[0, int8], 112]
} [size[NVME_TCP_ICRESP_PDU_SIZE]]

type nvme_tcp_hdr_icresp_pdu nvme_tcp_hdr_t_const[nvme_tcp_icresp, NVME_TCP_ICRESP_PDU_SIZE]

### End of nvme_tcp_icresp_pdu ###
#########################################

#########################################
### nvme_tcp_icreq_pdu ###

nvme_tcp_icreq_pdu {
	hdr	nvme_tcp_hdr_icreq_pdu
	pfv	flags[nvme_tcp_pfv, int16]
# Linux NVMe driver only support hcpda = 0
	hpda	const[0, int8]
	digest	flags[nvme_tcp_digest_option, int8]
	maxr2t	int32
	rsvd	array[const[0, int8], 112]
} [size[NVME_TCP_ICREQ_PDU_SIZE]]

type nvme_tcp_hdr_icreq_pdu nvme_tcp_hdr_t_const_plen[nvme_tcp_icreq, NVME_TCP_ICREQ_PDU_SIZE, NVME_TCP_ICREQ_PDU_SIZE]

### End of nvme_tcp_icreq_pdu ###
#########################################

#########################################
### nvme_tcp_cmd_pdu ###

nvme_tcp_cmd_pdu {
	hdr	nvme_tcp_hdr_cmd_pdu
	cmd	nvme_command
} [size[NVME_TCP_CMD_PDU_SIZE]]

type nvme_tcp_hdr_cmd_pdu nvme_tcp_hdr_t_const_plen[nvme_tcp_cmd, NVME_TCP_CMD_PDU_SIZE, NVME_TCP_CMD_PDU_SIZE]

nvme_command {
	anon_union	nvme_command_union
}

nvme_command_union [
	common		nvme_common_command
	rw		nvme_rw_command
	identify	nvme_identify
	features	nvme_features
	create_cq	nvme_create_cq
	create_sq	nvme_create_sq
	delete_queue	nvme_delete_queue
	dlfw		nvme_download_firmware
	format		nvme_format_cmd
	dsm		nvme_dsm_cmd
	write_zeroes	nvme_write_zeroes_cmd
	zms		nvme_zone_mgmt_send_cmd
	zmr		nvme_zone_mgmt_recv_cmd
	abort		nvme_abort_cmd
	fabrics		nvmf_common_command
	connect		nvmf_connect_command
	prop_set	nvmf_property_set_command
	prop_get	nvmf_property_get_command
	auth_common	nvmf_auth_common_command
	auth_send	nvmf_auth_send_command
	auth_receive	nvmf_auth_receive_command
	dbbuf		nvme_dbbuf
	directive	nvme_directive_cmd
	get_log_page	nvme_get_log_page_command
]

nvme_get_log_page_command {
	opcode		const[nvme_admin_get_log_page, int8]
	flags		int8
	command_id	int16
	nsid		int32
	rsvd2		array[const[0, int64], 2]
	dptr		nvme_data_ptr
	lid		int8
	lsp		int8
	numdl		int16
	numdu		int16
	rsvd11		const[0, int16]
	anon_union	nvme_get_log_page_command_anon_union
	rsvd14		array[const[0, int8], 3]
	csi		int8
	rsvd15		const[0, int32]
}

nvme_get_log_page_command_anon_union [
	anon_struct	nvme_get_log_page_command_anon_union_anon_struct
	lpo		int64
]

nvme_get_log_page_command_anon_union_anon_struct {
	lpol	int32
	lpou	int32
}

nvme_download_firmware {
	opcode		const[nvme_admin_download_fw, int8]
	flags		int8
	command_id	int16
	rsvd1		array[const[0, int32], 5]
	dptr		nvme_data_ptr
	numd		int32
	offset		int32
	rsvd12		array[const[0, int32], 4]
}

nvme_format_cmd {
	opcode		const[nvme_admin_format_nvm, int8]
	flags		int8
	command_id	int16
	nsid		int32
	rsvd2		array[const[0, int64], 4]
	cdw10		int32
	rsvd11		array[const[0, int32], 5]
}

nvme_dsm_cmd {
	opcode		const[nvme_cmd_dsm, int8]
	flags		int8
	command_id	int16
	nsid		int32
	rsvd2		array[const[0, int64], 2]
	dptr		nvme_data_ptr
	nr		int32
	attributes	int32
	rsvd12		array[const[0, int32], 4]
}

nvme_write_zeroes_cmd {
	opcode		const[nvme_cmd_write_zeroes, int8]
	flags		int8
	command_id	int16
	nsid		int32
	rsvd2		const[0, int64]
	metadata	int64
	dptr		nvme_data_ptr
	slba		int64
	length		int16
	control		int16
	dsmgmt		int32
	reftag		int32
	apptag		int16
	appmask		int16
}

nvme_zone_mgmt_send_cmd {
	opcode		const[nvme_cmd_zone_mgmt_send, int8]
	flags		int8
	command_id	int16
	nsid		int32
	cdw2		array[int32, 2]
	metadata	int64
	dptr		nvme_data_ptr
	slba		int64
	cdw12		int32
	zsa		int8
	select_all	int8
	rsvd13		array[const[0, int8], 2]
	cdw14		array[int32, 2]
}

nvme_zone_mgmt_recv_cmd {
	opcode		const[nvme_cmd_zone_mgmt_recv, int8]
	flags		int8
	command_id	int16
	nsid		int32
	rsvd2		array[const[0, int64], 2]
	dptr		nvme_data_ptr
	slba		int64
	numd		int32
	zra		int8
	zrasf		int8
	pr		int8
	rsvd13		const[0, int8]
	cdw14		array[int32, 2]
}

nvme_abort_cmd {
	opcode		const[nvme_admin_abort_cmd, int8]
	flags		int8
	command_id	int16
	rsvd1		array[const[0, int32], 9]
	sqid		int16
	cid		int16
	rsvd11		array[const[0, int32], 5]
}

nvmf_common_command {
	opcode		const[nvme_fabrics_command, int8]
	resv1		int8
	command_id	int16
	fctype		int8
	resv2		array[int8, 35]
	ts		array[int8, 24]
}

nvmf_connect_command {
	opcode		const[nvme_fabrics_command, int8]
	resv1		const[0x40, int8]
	command_id	const[0x0, int16]
	fctype		const[nvme_fabrics_type_connect, int8]
	resv2		array[int8, 19]
	dptr		nvme_data_ptr
	recfmt		int16
	qid		int16
	sqsize		int16
	cattr		int8
	resv3		int8
	kato		int32
	resv4		array[int8, 12]
}

nvmf_property_set_command {
	opcode		const[nvme_fabrics_command, int8]
	resv1		int8
	command_id	int16
	fctype		const[nvme_fabrics_type_property_set, int8]
	resv2		array[int8, 35]
	attrib		const[0x0, int8]
	resv3		array[int8, 3]
	offset		int32
	value		int64
	resv4		array[int8, 8]
}

nvmf_property_get_command {
	opcode		const[nvme_fabrics_command, int8]
	resv1		int8
	command_id	int16
	fctype		const[nvme_fabrics_type_property_get, int8]
	resv2		array[int8, 35]
	attrib		int8
	resv3		array[int8, 3]
	offset		int32
	resv4		array[int8, 16]
}

nvmf_auth_common_command {
	opcode		const[nvme_fabrics_command, int8]
	resv1		int8
	command_id	int16
	fctype		int8
	resv2		array[int8, 19]
	dptr		nvme_data_ptr
	resv3		int8
	spsp0		int8
	spsp1		int8
	secp		int8
	al_tl		int32
	resv4		array[int8, 16]
}

nvmf_auth_send_command {
	opcode		const[nvme_fabrics_command, int8]
	resv1		int8
	command_id	int16
	fctype		const[nvme_fabrics_type_auth_send, int8]
	resv2		array[int8, 19]
	dptr		nvme_data_ptr
	resv3		int8
	spsp0		flags[sps_flags, int8]
	spsp1		flags[sps_flags, int8]
	secp		flags[secp_flags, int8]
	tl		int32
	resv4		array[int8, 16]
}

secp_flags = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER, 0x0
sps_flags = 0x1, 0x2

nvmf_auth_receive_command {
	opcode		const[nvme_fabrics_command, int8]
	resv1		int8
	command_id	int16
	fctype		const[nvme_fabrics_type_auth_receive, int8]
	resv2		array[int8, 19]
	dptr		nvme_data_ptr
	resv3		int8
	spsp0		flags[sps_flags, int8]
	spsp1		flags[sps_flags, int8]
	secp		flags[secp_flags, int8]
	al		int32
	resv4		array[int8, 16]
}

nvme_dbbuf {
	opcode		const[nvme_admin_dbbuf, int8]
	flags		int8
	command_id	int16
	rsvd1		array[const[0, int32], 5]
	prp1		int64
	prp2		int64
	rsvd12		array[const[0, int32], 6]
}

nvme_directive_cmd {
	opcode		flags[nvme_admin_directive_opcodes, int8]
	flags		int8
	command_id	int16
	nsid		int32
	rsvd2		array[const[0, int64], 2]
	dptr		nvme_data_ptr
	numd		int32
	doper		int8
	dtype		int8
	dspec		int16
	endir		int8
	tdtype		int8
	rsvd15		const[0, int16]
	rsvd16		array[int32, 3]
}

nvme_admin_directive_opcodes = nvme_admin_directive_send, nvme_admin_directive_recv
#####################
#####################

nvme_delete_queue {
	opcode		const[nvme_admin_delete_cq, int8]
	flags		int8
	command_id	int16
	rsvd1		array[const[0, int32], 9]
	qid		int16
	rsvd10		const[0, int16]
	rsvd11		array[const[0, int32], 5]
}

nvme_create_sq {
	opcode		const[nvme_admin_create_sq, int8]
	flags		int8
	command_id	int16
	rsvd1		array[const[0, int32], 5]
	prp1		int64
	rsvd8		const[0, int64]
	sqid		int16
	qsize		int16
	sq_flags	int16
	cqid		int16
	rsvd12		array[const[0, int32], 4]
}

nvme_create_cq {
	opcode		const[nvme_admin_create_cq, int8]
	flags		int8
	command_id	int16
	rsvd1		array[const[0, int32], 5]
	prp1		int64
	rsvd8		const[0, int64]
	cqid		int16
	qsize		int16
	cq_flags	int16
	irq_vector	int16
	rsvd12		array[const[0, int32], 4]
}

nvme_features {
	opcode		flags[nvme_features_opcodes, int8]
	flags		int8
	command_id	int16
	nsid		int32
	rsvd2		array[const[0, int64], 2]
	dptr		nvme_data_ptr
	fid		int32
	dword11		int32
	dword12		int32
	dword13		int32
	dword14		int32
	dword15		int32
}

nvme_features_opcodes = nvme_admin_set_features, nvme_admin_get_features

nvme_identify {
	opcode		const[nvme_admin_identify, int8]
	flags		int8
	command_id	int16
	nsid		int32
	rsvd2		array[const[0, int64], 2]
	dptr		nvme_data_ptr
	cns		int8
	rsvd3		const[0, int8]
	ctrlid		int16
	rsvd11		array[const[0, int8], 3]
	csi		int8
	rsvd12		array[const[0, int32], 4]
}

nvme_common_command {
	opcode		flags[nvme_common_command_opcodes, int8]
	flags		int8
	command_id	int16
	nsid		int32
	cdw2		array[int32, 2]
	metadata	int64
	dptr		nvme_data_ptr
	cdws		cdws_struct_group
}

nvme_common_command_opcodes = nvme_cmd_resv_report, nvme_fabrics_command, nvme_cmd_write, nvme_admin_async_event, nvme_cmd_flush, nvme_cmd_read, nvme_cmd_write_zeroes, nvme_cmd_dsm, nvme_admin_security_recv, nvme_admin_security_send, nvme_cmd_flush, nvme_admin_get_log_page, nvme_admin_identify, nvme_admin_abort_cmd, nvme_admin_set_features, nvme_admin_get_features, nvme_admin_async_event, nvme_admin_keep_alive

cdws_struct_group [
	cdws_anon	cdws_anon_struct
	cdws		cdws_anon_struct
]

cdws_anon_struct {
	cdw10	int32
	cdw11	int32
	cdw12	int32
	cdw13	int32
	cdw14	int32
	cdw15	int32
}

nvme_data_ptr [
	anon_struct	nvme_data_ptr_anon_struct
	sgl		nvme_sgl_desc
	ksgl		nvme_keyed_sgl_desc
]

nvme_data_ptr_anon_struct {
	prp1	int64
	prp2	int64
} [packed]

nvme_sgl_desc {
	addr	int64
	length	int32
	rsvd	array[const[0, int8], 3]
	type	int8
} [packed]

nvme_keyed_sgl_desc {
	addr	int64
	length	array[int8, 3]
	key	array[int8, 4]
	type	int8
} [packed]

nvme_rw_command {
	opcode		flags[nvme_opcodes_rw, int8]
	flags		int8
	command_id	int16
	nsid		int32
	cdw2		int32
	cdw3		int32
	metadata	int64
	dptr		nvme_data_ptr
	slba		int64
	length		int16
	control		int16
	dsmgmt		int32
	reftag		int32
	apptag		int16
	appmask		int16
}

nvme_opcodes_rw = nvme_cmd_write, nvme_cmd_read, nvme_cmd_zone_append

### End of nvme_tcp_cmd_pdu ###
#########################################

#########################################

resource nvme_sock[sock]

syz_socket_connect_nvme_tcp() nvme_sock

sendto$inet_nvme_of_msg(fd nvme_sock, pdu ptr[in, nvme_of_msg], len len[pdu], f const[0], addr const[0], addrlen const[0])
sendto$inet_nvme_pdu(fd nvme_sock, pdu ptr[in, nvme_tcp_pdu], len len[pdu], f const[0], addr const[0], addrlen const[0])
sendto$inet_nvme_icreq_pdu(fd nvme_sock, pdu ptr[in, nvme_tcp_icreq_pdu], len len[pdu], f const[0], addr const[0], addrlen const[0])
recvmsg$inet_nvme(fd nvme_sock, msg ptr[inout, recv_msghdr], f flags[recv_flags])
recvfrom$inet_nvme(fd nvme_sock, buf buffer[out], len len[buf], f flags[recv_flags], addr ptr[in, sockaddr_storage, opt], addrlen len[addr])
