# 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 <uapi/linux/fs.h>
include <uapi/linux/blktrace_api.h>
include <uapi/linux/blkzoned.h>
include <uapi/linux/pr.h>
include <uapi/linux/hdreg.h>
include <uapi/linux/blkpg.h>
include <linux/blk-crypto.h>

resource fd_block_trace[fd]
resource fd_block[fd_block_trace]

openat$nullb(fd const[AT_FDCWD], file ptr[in, string["/dev/nullb0"]], flags flags[open_flags], mode const[0]) fd_block
openat$rnullb(fd const[AT_FDCWD], file ptr[in, rnull_dev_node], flags flags[open_flags], mode const[0]) fd_block
openat$pmem0(fd const[AT_FDCWD], file ptr[in, string["/dev/pmem0"]], flags flags[open_flags], mode const[0]) fd_block

# Rust null block driver (drivers/block/rnull) creates devices via configfs:
# mkdir /sys/kernel/config/rnull/NAME, configure the attributes, then write 1 to
# the power attribute to create /dev/NAME. Use per-proc names so that parallel
# test processes do not interfere with each other.
resource fd_rnull_attr[fd]

mkdirat$rnull(fd const[AT_FDCWD], path ptr[in, rnull_path[const[0, int8]]], mode const[0x1ff])
unlinkat$rnull(fd const[AT_FDCWD], path ptr[in, rnull_path[const[0, int8]]], flags const[AT_REMOVEDIR])
openat$rnull_attr(fd const[AT_FDCWD], file ptr[in, rnull_path[string[rnull_attrs]]], flags const[O_RDWR], mode const[0]) fd_rnull_attr
write$rnull_attr(fd fd_rnull_attr, buf ptr[in, stringnoz[rnull_attr_vals]], len bytesize[buf])

type rnull_path[SUFFIX] {
	prefix	stringnoz["/sys/kernel/config/rnull/syzrnull"]
# NEED: this does not support formatting numbers larger than 9.
	id	proc['0', 1, int8]
	suffix	SUFFIX
} [packed]

rnull_dev_node {
	prefix	stringnoz["/dev/syzrnull"]
	id	proc['0', 1, int8]
	z	const[0, int8]
} [packed]

rnull_attrs = "/power", "/blocksize", "/rotational", "/size", "/irqmode"
# Booleans (power, rotational), irqmode (0, 1), blocksize and size (in MiB).
rnull_attr_vals = "0", "1", "n", "y", "off", "on", "512", "1024", "2048", "4096"

ioctl$BLKTRACESETUP(fd fd_block_trace, cmd const[BLKTRACESETUP], arg ptr[inout, blk_user_trace_setup])
ioctl$BLKTRACESTART(fd fd_block_trace, cmd const[BLKTRACESTART], arg const[0])
ioctl$BLKTRACESTOP(fd fd_block_trace, cmd const[BLKTRACESTOP], arg const[0])
ioctl$BLKTRACETEARDOWN(fd fd_block_trace, cmd const[BLKTRACETEARDOWN], arg const[0])

ioctl$BLKFLSBUF(fd fd_block, cmd const[BLKFLSBUF], arg ptr[in, intptr])
ioctl$BLKROSET(fd fd_block, cmd const[BLKROSET], arg ptr[in, intptr])
ioctl$BLKDISCARD(fd fd_block, cmd const[BLKDISCARD], arg ptr[in, intptr])
ioctl$BLKSECDISCARD(fd fd_block, cmd const[BLKSECDISCARD], arg ptr[in, intptr])
ioctl$BLKDISCARDZEROES(fd fd_block, cmd const[BLKDISCARDZEROES], arg ptr[out, intptr])
ioctl$BLKZEROOUT(fd fd_block, cmd const[BLKZEROOUT], arg ptr[in, blk_zone_range])
ioctl$BLKRAGET(fd fd_block, cmd const[BLKRAGET], arg ptr[out, intptr])
ioctl$BLKFRAGET(fd fd_block, cmd const[BLKFRAGET], arg ptr[out, intptr])
ioctl$BLKROGET(fd fd_block, cmd const[BLKROGET], arg ptr[out, intptr])
ioctl$BLKBSZGET(fd fd_block, cmd const[BLKBSZGET], arg ptr[out, intptr])
ioctl$BLKSSZGET(fd fd_block, cmd const[BLKSSZGET], arg ptr[out, intptr])
ioctl$BLKPBSZGET(fd fd_block, cmd const[BLKPBSZGET], arg ptr[out, intptr])
ioctl$BLKIOMIN(fd fd_block, cmd const[BLKIOMIN], arg ptr[out, intptr])
ioctl$BLKIOOPT(fd fd_block, cmd const[BLKIOOPT], arg ptr[out, intptr])
ioctl$BLKALIGNOFF(fd fd_block, cmd const[BLKALIGNOFF], arg ptr[out, intptr])
ioctl$BLKSECTGET(fd fd_block, cmd const[BLKSECTGET], arg ptr[out, intptr])
ioctl$BLKROTATIONAL(fd fd_block, cmd const[BLKROTATIONAL], arg ptr[out, intptr])
ioctl$BLKRASET(fd fd_block, cmd const[BLKRASET], arg ptr[in, intptr])
ioctl$BLKFRASET(fd fd_block, cmd const[BLKFRASET], arg ptr[in, intptr])
ioctl$BLKBSZSET(fd fd_block, cmd const[BLKBSZSET], arg ptr[in, intptr])
ioctl$BLKPG(fd fd_block, cmd const[BLKPG], arg ptr[in, blkpg_ioctl_arg])
ioctl$BLKRRPART(fd fd_block, cmd const[BLKRRPART], arg const[0])
ioctl$BLKGETSIZE(fd fd_block, cmd const[BLKGETSIZE], arg ptr[out, intptr])
ioctl$BLKGETSIZE64(fd fd_block, cmd const[BLKGETSIZE64], arg ptr[out, int64])
ioctl$BLKGETDISKSEQ(fd fd_block, cmd const[BLKGETDISKSEQ], arg ptr[out, int64])

# TODO: To properly explore zones-related ioctls, figure out how to setup zoned devices.
ioctl$BLKREPORTZONE(fd fd_block, cmd const[BLKREPORTZONE], arg ptr[inout, blk_zone_report])
ioctl$BLKRESETZONE(fd fd_block, cmd const[BLKRESETZONE], arg ptr[in, blk_zone_range])
ioctl$BLKOPENZONE(fd fd_block, cmd const[BLKOPENZONE], arg ptr[in, blk_zone_range])
ioctl$BLKCLOSEZONE(fd fd_block, cmd const[BLKCLOSEZONE], arg ptr[in, blk_zone_range])
ioctl$BLKFINISHZONE(fd fd_block, cmd const[BLKFINISHZONE], arg ptr[in, blk_zone_range])
ioctl$BLKGETZONESZ(fd fd_block, cmd const[BLKGETZONESZ], arg ptr[in, int32])
ioctl$BLKGETNRZONES(fd fd_block, cmd const[BLKGETNRZONES], arg ptr[in, int32])

# TODO: These calls may fail early on current kernel testing setups due to
# uninitialized blk_crypto_profile involved in related device drivers.
ioctl$BLKCRYPTOIMPORTKEY(fd fd_block, cmd const[BLKCRYPTOIMPORTKEY], arg ptr[inout, blk_crypto_import_key_arg])
ioctl$BLKCRYPTOGENERATEKEY(fd fd_block, cmd const[BLKCRYPTOGENERATEKEY], arg ptr[inout, blk_crypto_generate_key_arg])
ioctl$BLKCRYPTOPREPAREKEY(fd fd_block, cmd const[BLKCRYPTOPREPAREKEY], arg ptr[inout, blk_crypto_prepare_key_arg])

ioctl$HDIO_GETGEO(fd fd_block, cmd const[HDIO_GETGEO], arg ptr[out, hd_geometry])

ioctl$IOC_PR_REGISTER(fd fd_block, cmd const[IOC_PR_REGISTER], arg ptr[in, pr_registration])
ioctl$IOC_PR_RESERVE(fd fd_block, cmd const[IOC_PR_RESERVE], arg ptr[in, pr_reservation])
ioctl$IOC_PR_RELEASE(fd fd_block, cmd const[IOC_PR_RELEASE], arg ptr[in, pr_reservation])
ioctl$IOC_PR_PREEMPT(fd fd_block, cmd const[IOC_PR_PREEMPT], arg ptr[in, pr_preempt])
ioctl$IOC_PR_PREEMPT_ABORT(fd fd_block, cmd const[IOC_PR_PREEMPT_ABORT], arg ptr[in, pr_preempt])
ioctl$IOC_PR_CLEAR(fd fd_block, cmd const[IOC_PR_CLEAR], arg ptr[in, pr_clear])

blk_zone_report {
	sector		int64
	nr_zones	len[zones, int32]
	reserved	const[0, int32]
	zones		array[blk_zone]
} [packed]

blk_zone {
	start		int64
	len		int64
	wp		int64
	type		int8
	cond		int8
	non_seq		int8
	reset		int8
	resv		array[const[0, int8], 4]
	capacity	int64
	reserved	array[const[0, int8], 24]
}

blk_zone_range {
	sector		int64
	nr_sectors	int64
}

hd_geometry {
	heads		int8
	sectors		int8
	cylinders	int16
	start		intptr
}

blkpg_ioctl_arg {
	op	flags[blkpg_ioctl_op, int32]
	flags	const[0, int32]
	datalen	bytesize[data, int32]
	data	ptr[in, blkpg_partition]
}

blkpg_partition {
	start	int64
	length	int64
	pno	int32[0:20]
	devname	array[const[0, int8], BLKPG_DEVNAMELTH]
	volname	array[const[0, int8], BLKPG_VOLNAMELTH]
}

blkpg_ioctl_op = BLKPG_ADD_PARTITION, BLKPG_DEL_PARTITION, BLKPG_RESIZE_PARTITION

blk_user_trace_setup {
	name		array[const[0, int8], BLKTRACE_BDEV_SIZE]
	act_mask	int16
	buf_size	int32
	buf_nr		int32
	start_lba	int64
	end_lba		int64
	pid		pid
}

blk_crypto_import_key_arg {
	raw_key_ptr	ptr[in, array[int8, 16:BLK_CRYPTO_MAX_RAW_KEY_SIZE]]
	raw_key_size	len[raw_key_ptr, int64]
	lt_key_ptr	ptr[out, array[int8, 1:BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]]
	lt_key_size	len[lt_key_ptr, int64]
	reserved	array[const[0, int64], 4]
}

blk_crypto_generate_key_arg {
	lt_key_ptr	ptr[out, array[int8, 1:BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]]
	lt_key_size	len[lt_key_ptr, int64]
	reserved	array[const[0, int64], 4]
}

blk_crypto_prepare_key_arg {
	lt_key_ptr	ptr[in, array[int8, 1:BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]]
	lt_key_size	len[lt_key_ptr, int64]
	eph_key_ptr	ptr[out, array[int8, 1:BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]]
	eph_key_size	len[eph_key_ptr, int64]
	reserved	array[const[0, int64], 4]
}

pr_registration {
	old_key	int64
	new_key	int64
	flags	bool32
	pad	const[0, int32]
}

pr_reservation {
	key	int64
	type	int32
	flags	bool32
}

pr_preempt {
	old_key	int64
	new_key	int64
	type	int32
	flags	int32
}

pr_clear {
	key	int64
	flags	const[0, int32]
	pad	const[0, int32]
}
