code:
============================
Summary of CDROM ioctl calls
============================

- Edward A. Falk <efalk@google.com>

November, 2004

This document attempts to describe the ioctl(2) calls supported by
the CDROM layer.  These are by-and-large implemented (as of Linux 2.6)
in drivers/cdrom/cdrom.c and drivers/block/scsi_ioctl.c

ioctl values are listed in <linux/cdrom.h>.  As of this writing, they
are as follows:

	========================  ===============================================
	CDROMPAUSE		  Pause Audio Operation
	CDROMRESUME		  Resume paused Audio Operation
	CDROMPLAYMSF		  Play Audio MSF (struct cdrom_msf)
	CDROMPLAYTRKIND		  Play Audio Track/index (struct cdrom_ti)
	CDROMREADTOCHDR		  Read TOC header (struct cdrom_tochdr)
	CDROMREADTOCENTRY	  Read TOC entry (struct cdrom_tocentry)
	CDROMSTOP		  Stop the cdrom drive
	CDROMSTART		  Start the cdrom drive
	CDROMEJECT		  Ejects the cdrom media
	CDROMVOLCTRL		  Control output volume (struct cdrom_volctrl)
	CDROMSUBCHNL		  Read subchannel data (struct cdrom_subchnl)
	CDROMREADMODE2		  Read CDROM mode 2 data (2336 Bytes)
				  (struct cdrom_read)
	CDROMREADMODE1		  Read CDROM mode 1 data (2048 Bytes)
				  (struct cdrom_read)
	CDROMREADAUDIO		  (struct cdrom_read_audio)
	CDROMEJECT_SW		  enable(1)/disable(0) auto-ejecting
	CDROMMULTISESSION	  Obtain the start-of-last-session
				  address of multi session disks
				  (struct cdrom_multisession)
	CDROM_GET_MCN		  Obtain the "Universal Product Code"
				  if available (struct cdrom_mcn)
	CDROM_GET_UPC		  Deprecated, use CDROM_GET_MCN instead.
	CDROMRESET		  hard-reset the drive
	CDROMVOLREAD		  Get the drive's volume setting
				  (struct cdrom_volctrl)
	CDROMREADRAW		  read data in raw mode (2352 Bytes)
				  (struct cdrom_read)
	CDROMREADCOOKED		  read data in cooked mode
	CDROMSEEK		  seek msf address
	CDROMPLAYBLK		  scsi-cd only, (struct cdrom_blk)
	CDROMREADALL		  read all 2646 bytes
	CDROMGETSPINDOWN	  return 4-bit spindown value
	CDROMSETSPINDOWN	  set 4-bit spindown value
	CDROMCLOSETRAY		  pendant of CDROMEJECT
	CDROM_SET_OPTIONS	  Set behavior options
	CDROM_CLEAR_OPTIONS	  Clear behavior options
	CDROM_SELECT_SPEED	  Set the CD-ROM speed
	CDROM_SELECT_DISC	  Select disc (for juke-boxes)
	CDROM_MEDIA_CHANGED	  Check is media changed
	CDROM_TIMED_MEDIA_CHANGE  Check if media changed
				  since given time
				  (struct cdrom_timed_media_change_info)
	CDROM_DRIVE_STATUS	  Get tray position, etc.
	CDROM_DISC_STATUS	  Get disc type, etc.
	CDROM_CHANGER_NSLOTS	  Get number of slots
	CDROM_LOCKDOOR		  lock or unlock door
	CDROM_DEBUG		  Turn debug messages on/off
	CDROM_GET_CAPABILITY	  get capabilities
	CDROMAUDIOBUFSIZ	  set the audio buffer size
	DVD_READ_STRUCT		  Read structure
	DVD_WRITE_STRUCT	  Write structure
	DVD_AUTH		  Authentication
	CDROM_SEND_PACKET	  send a packet to the drive
	CDROM_NEXT_WRITABLE	  get next writable block
	CDROM_LAST_WRITTEN	  get last block written on disc
	========================  ===============================================

The information that follows was determined from reading kernel source
code.  It is likely that some corrections will be made over time.

------------------------------------------------------------------------------

General:

	Unless otherwise specified, all ioctl calls return 0 on success
	and -1 with errno set to an appropriate value on error.  (Some
	ioctls return non-negative data values.)

	Unless otherwise specified, all ioctl calls return -1 and set
	errno to EFAULT on a failed attempt to copy data to or from user
	address space.

	Individual drivers may return error codes not listed here.

	Unless otherwise specified, all data structures and constants
	are defined in <linux/cdrom.h>

------------------------------------------------------------------------------

CDROMPAUSE
	Pause Audio Operation

	usage::

	  ioctl(fd, CDROMPAUSE, 0);

	inputs:
		none

	outputs:
		none

	error return:
	  - ENOSYS	cd drive not audio-capable.

CDROMRESUME
	Resume paused Audio Operation

	usage::

	  ioctl(fd, CDROMRESUME, 0);

	inputs:
		none

	outputs:
		none

	error return:
	  - ENOSYS	cd drive not audio-capable.

CDROMPLAYMSF
	Play Audio MSF

	(struct cdrom_msf)

	usage::

	  struct cdrom_msf msf;

	  ioctl(fd, CDROMPLAYMSF, &msf);

	inputs:
		cdrom_msf structure, describing a segment of music to play

	outputs:
		none

	error return:
	  - ENOSYS	cd drive not audio-capable.

	notes:
		- MSF stands for minutes-seconds-frames
		- LBA stands for logical block address
		- Segment is described as start and end times, where each time
		  is described as minutes:seconds:frames.
		  A frame is 1/75 of a second.

CDROMPLAYTRKIND
	Play Audio Track/index

	(struct cdrom_ti)

	usage::

	  struct cdrom_ti ti;

	  ioctl(fd, CDROMPLAYTRKIND, &ti);

	inputs:
		cdrom_ti structure, describing a segment of music to play

	outputs:
		none

	error return:
	  - ENOSYS	cd drive not audio-capable.

	notes:
		- Segment is described as start and end times, where each time
		  is described as a track and an index.

CDROMREADTOCHDR
	Read TOC header

	(struct cdrom_tochdr)

	usage::

	  cdrom_tochdr header;

	  ioctl(fd, CDROMREADTOCHDR, &header);

	inputs:
		cdrom_tochdr structure

	outputs:
		cdrom_tochdr structure

	error return:
	  - ENOSYS	cd drive not audio-capable.

CDROMREADTOCENTRY
	Read TOC entry

	(struct cdrom_tocentry)

	usage::

	  struct cdrom_tocentry entry;

	  ioctl(fd, CDROMREADTOCENTRY, &entry);

	inputs:
		cdrom_tocentry structure

	outputs:
		cdrom_tocentry structure

	error return:
	  - ENOSYS	cd drive not audio-capable.
	  - EINVAL	entry.cdte_format not CDROM_MSF or CDROM_LBA
	  - EINVAL	requested track out of bounds
	  - EIO		I/O error reading TOC

	notes:
		- TOC stands for Table Of Contents
		- MSF stands for minutes-seconds-frames
		- LBA stands for logical block address

CDROMSTOP
	Stop the cdrom drive

	usage::

	  ioctl(fd, CDROMSTOP, 0);

	inputs:
		none

	outputs:
		none

	error return:
	  - ENOSYS	cd drive not audio-capable.

	notes:
	  - Exact interpretation of this ioctl depends on the device,
	    but most seem to spin the drive down.

CDROMSTART
	Start the cdrom drive

	usage::

	  ioctl(fd, CDROMSTART, 0);

	inputs:
		none

	outputs:
		none

	error return:
	  - ENOSYS	cd drive not audio-capable.

	notes:
	  - Exact interpretation of this ioctl depends on the device,
	    but most seem to spin the drive up and/or close the tray.
	    Other devices ignore the ioctl completely.

CDROMEJECT
	- Ejects the cdrom media

	usage::

	  ioctl(fd, CDROMEJECT, 0);

	inputs:
		none

	outputs:
		none

	error returns:
	  - ENOSYS	cd drive not capable of ejecting
	  - EBUSY	other processes are accessing drive, or door is locked

	notes:
		- See CDROM_LOCKDOOR, below.

CDROMCLOSETRAY
	pendant of CDROMEJECT

	usage::

	  ioctl(fd, CDROMCLOSETRAY, 0);

	inputs:
		none

	outputs:
		none

	error returns:
	  - ENOSYS	cd drive not capable of closing the tray
	  - EBUSY	other processes are accessing drive, or door is locked

	notes:
		- See CDROM_LOCKDOOR, below.

CDROMVOLCTRL
	Control output volume (struct cdrom_volctrl)

	usage::

	  struct cdrom_volctrl volume;

	  ioctl(fd, CDROMVOLCTRL, &volume);

	inputs:
		cdrom_volctrl structure containing volumes for up to 4
		channels.

	outputs:
		none

	error return:
	  - ENOSYS	cd drive not audio-capable.

CDROMVOLREAD
	Get the drive's volume setting

	(struct cdrom_volctrl)

	usage::

	  struct cdrom_volctrl volume;

	  ioctl(fd, CDROMVOLREAD, &volume);

	inputs:
		none

	outputs:
		The current volume settings.

	error return:
	  - ENOSYS	cd drive not audio-capable.

CDROMSUBCHNL
	Read subchannel data

	(struct cdrom_subchnl)

	usage::

	  struct cdrom_subchnl q;

	  ioctl(fd, CDROMSUBCHNL, &q);

	inputs:
		cdrom_subchnl structure

	outputs:
		cdrom_subchnl structure

	error return:
	  - ENOSYS	cd drive not audio-capable.
	  - EINVAL	format not CDROM_MSF or CDROM_LBA

	notes:
		- Format is converted to CDROM_MSF or CDROM_LBA
		  as per user request on return

CDROMREADRAW
	read data in raw mode (2352 Bytes)

	(struct cdrom_read)

	usage::

	  union {

	    struct cdrom_msf msf;		/* input */
	    char buffer[CD_FRAMESIZE_RAW];	/* return */
	  } arg;
	  ioctl(fd, CDROMREADRAW, &arg);

	inputs:
		cdrom_msf structure indicating an address to read.

		Only the start values are significant.

	outputs:
		Data written to address provided by user.

	error return:
	  - EINVAL	address less than 0, or msf less than 0:2:0
	  - ENOMEM	out of memory

	notes:
		- As of 2.6.8.1, comments in <linux/cdrom.h> indicate that this
		  ioctl accepts a cdrom_read structure, but actual source code
		  reads a cdrom_msf structure and writes a buffer of data to
		  the same address.

		- MSF values are converted to LBA values via this formula::

		    lba = (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;

CDROMREADMODE1
	Read CDROM mode 1 data (2048 Bytes)

	(struct cdrom_read)

	notes:
		Identical to CDROMREADRAW except that block size is
		CD_FRAMESIZE (2048) bytes

CDROMREADMODE2
	Read CDROM mode 2 data (2336 Bytes)

	(struct cdrom_read)

	notes:
		Identical to CDROMREADRAW except that block size is
		CD_FRAMESIZE_RAW0 (2336) bytes

CDROMREADAUDIO
	(struct cdrom_read_audio)

	usage::

	  struct cdrom_read_audio ra;

	  ioctl(fd, CDROMREADAUDIO, &ra);

	inputs:
		cdrom_read_audio structure containing read start
		point and length

	outputs:
		audio data, returned to buffer indicated by ra

	error return:
	  - EINVAL	format not CDROM_MSF or CDROM_LBA
	  - EINVAL	nframes not in range [1 75]
	  - ENXIO	drive has no queue (probably means invalid fd)
	  - ENOMEM	out of memory

CDROMEJECT_SW
	enable(1)/disable(0) auto-ejecting

	usage::

	  int val;

	  ioctl(fd, CDROMEJECT_SW, val);

	inputs:
		Flag specifying auto-eject flag.

	outputs:
		none

	error return:
	  - ENOSYS	Drive is not capable of ejecting.
	  - EBUSY	Door is locked

CDROMMULTISESSION
	Obtain the start-of-last-session address of multi session disks

	(struct cdrom_multisession)

	usage::

	  struct cdrom_multisession ms_info;

	  ioctl(fd, CDROMMULTISESSION, &ms_info);

	inputs:
		cdrom_multisession structure containing desired

	  format.

	outputs:
		cdrom_multisession structure is filled with last_session
		information.

	error return:
	  - EINVAL	format not CDROM_MSF or CDROM_LBA

CDROM_GET_MCN
	Obtain the "Universal Product Code"
	if available

	(struct cdrom_mcn)

	usage::

	  struct cdrom_mcn mcn;

	  ioctl(fd, CDROM_GET_MCN, &mcn);

	inputs:
		none

	outputs:
		Universal Product Code

	error return:
	  - ENOSYS	Drive is not capable of reading MCN data.

	notes:
		- Source code comments state::

		    The following function is implemented, although very few
		    audio discs give Universal Product Code information, which
		    should just be the Medium Catalog Number on the box.  Note,
		    that the way the code is written on the CD is /not/ uniform
		    across all discs!

CDROM_GET_UPC
	CDROM_GET_MCN  (deprecated)

	Not implemented, as of 2.6.8.1

CDROMRESET
	hard-reset the drive

	usage::

	  ioctl(fd, CDROMRESET, 0);

	inputs:
		none

	outputs:
		none

	error return:
	  - EACCES	Access denied:  requires CAP_SYS_ADMIN
	  - ENOSYS	Drive is not capable of resetting.

CDROMREADCOOKED
	read data in cooked mode

	usage::

	  u8 buffer[CD_FRAMESIZE]

	  ioctl(fd, CDROMREADCOOKED, buffer);

	inputs:
		none

	outputs:
		2048 bytes of data, "cooked" mode.

	notes:
		Not implemented on all drives.

CDROMREADALL
	read all 2646 bytes

	Same as CDROMREADCOOKED, but reads 2646 bytes.

CDROMSEEK
	seek msf address

	usage::

	  struct cdrom_msf msf;

	  ioctl(fd, CDROMSEEK, &msf);

	inputs:
		MSF address to seek to.

	outputs:
		none

CDROMPLAYBLK
	scsi-cd only

	(struct cdrom_blk)

	usage::

	  struct cdrom_blk blk;

	  ioctl(fd, CDROMPLAYBLK, &blk);

	inputs:
		Region to play

	outputs:
		none

CDROMGETSPINDOWN
	Obsolete, was ide-cd only

	usage::

	  char spindown;

	  ioctl(fd, CDROMGETSPINDOWN, &spindown);

	inputs:
		none

	outputs:
		The value of the current 4-bit spindown value.

CDROMSETSPINDOWN
	Obsolete, was ide-cd only

	usage::

	  char spindown

	  ioctl(fd, CDROMSETSPINDOWN, &spindown);

	inputs:
		4-bit value used to control spindown (TODO: more detail here)

	outputs:
		none

CDROM_SET_OPTIONS
	Set behavior options

	usage::

	  int options;

	  ioctl(fd, CDROM_SET_OPTIONS, options);

	inputs:
		New values for drive options.  The logical 'or' of:

	    ==============      ==================================
	    CDO_AUTO_CLOSE	close tray on first open(2)
	    CDO_AUTO_EJECT	open tray on last release
	    CDO_USE_FFLAGS	use O_NONBLOCK information on open
	    CDO_LOCK		lock tray on open files
	    CDO_CHECK_TYPE	check type on open for data
	    ==============      ==================================

	outputs:
		Returns the resulting options settings in the
		ioctl return value.  Returns -1 on error.

	error return:
	  - ENOSYS	selected option(s) not supported by drive.

CDROM_CLEAR_OPTIONS
	Clear behavior options

	Same as CDROM_SET_OPTIONS, except that selected options are
	turned off.

CDROM_SELECT_SPEED
	Set the CD-ROM speed

	usage::

	  int speed;

	  ioctl(fd, CDROM_SELECT_SPEED, speed);

	inputs:
		New drive speed.

	outputs:
		none

	error return:
	  - ENOSYS	speed selection not supported by drive.

CDROM_SELECT_DISC
	Select disc (for juke-boxes)

	usage::

	  int disk;

	  ioctl(fd, CDROM_SELECT_DISC, disk);

	inputs:
		Disk to load into drive.

	outputs:
		none

	error return:
	  - EINVAL	Disk number beyond capacity of drive

CDROM_MEDIA_CHANGED
	Check is media changed

	usage::

	  int slot;

	  ioctl(fd, CDROM_MEDIA_CHANGED, slot);

	inputs:
		Slot number to be tested, always zero except for jukeboxes.

		May also be special values CDSL_NONE or CDSL_CURRENT

	outputs:
		Ioctl return value is 0 or 1 depending on whether the media

	  has been changed, or -1 on error.

	error returns:
	  - ENOSYS	Drive can't detect media change
	  - EINVAL	Slot number beyond capacity of drive
	  - ENOMEM	Out of memory

CDROM_DRIVE_STATUS
	Get tray position, etc.

	usage::

	  int slot;

	  ioctl(fd, CDROM_DRIVE_STATUS, slot);

	inputs:
		Slot number to be tested, always zero except for jukeboxes.

		May also be special values CDSL_NONE or CDSL_CURRENT

	outputs:
		Ioctl return value will be one of the following values

	  from <linux/cdrom.h>:

	    =================== ==========================
	    CDS_NO_INFO		Information not available.
	    CDS_NO_DISC
	    CDS_TRAY_OPEN
	    CDS_DRIVE_NOT_READY
	    CDS_DISC_OK
	    -1			error
	    =================== ==========================

	error returns:
	  - ENOSYS	Drive can't detect drive status
	  - EINVAL	Slot number beyond capacity of drive
	  - ENOMEM	Out of memory

CDROM_DISC_STATUS
	Get disc type, etc.

	usage::

	  ioctl(fd, CDROM_DISC_STATUS, 0);

	inputs:
		none

	outputs:
		Ioctl return value will be one of the following values

	  from <linux/cdrom.h>:

	    - CDS_NO_INFO
	    - CDS_AUDIO
	    - CDS_MIXED
	    - CDS_XA_2_2
	    - CDS_XA_2_1
	    - CDS_DATA_1

	error returns:
		none at present

	notes:
	    - Source code comments state::

		Ok, this is where problems start.  The current interface for
		the CDROM_DISC_STATUS ioctl is flawed.  It makes the false
		assumption that CDs are all CDS_DATA_1 or all CDS_AUDIO, etc.
		Unfortunately, while this is often the case, it is also
		very common for CDs to have some tracks with data, and some
		tracks with audio.	Just because I feel like it, I declare
		the following to be the best way to cope.  If the CD has
		ANY data tracks on it, it will be returned as a data CD.
		If it has any XA tracks, I will return it as that.	Now I
		could simplify this interface by combining these returns with
		the above, but this more clearly demonstrates the problem
		with the current interface.  Too bad this wasn't designed
		to use bitmasks...	       -Erik

		Well, now we have the option CDS_MIXED: a mixed-type CD.
		User level programmers might feel the ioctl is not very
		useful.
				---david

CDROM_CHANGER_NSLOTS
	Get number of slots

	usage::

	  ioctl(fd, CDROM_CHANGER_NSLOTS, 0);

	inputs:
		none

	outputs:
		The ioctl return value will be the number of slots in a
		CD changer.  Typically 1 for non-multi-disk devices.

	error returns:
		none

CDROM_LOCKDOOR
	lock or unlock door

	usage::

	  int lock;

	  ioctl(fd, CDROM_LOCKDOOR, lock);

	inputs:
		Door lock flag, 1=lock, 0=unlock

	outputs:
		none

	error returns:
	  - EDRIVE_CANT_DO_THIS

				Door lock function not supported.
	  - EBUSY

				Attempt to unlock when multiple users
				have the drive open and not CAP_SYS_ADMIN

	notes:
		As of 2.6.8.1, the lock flag is a global lock, meaning that
		all CD drives will be locked or unlocked together.  This is
		probably a bug.

		The EDRIVE_CANT_DO_THIS value is defined in <linux/cdrom.h>
		and is currently (2.6.8.1) the same as EOPNOTSUPP

CDROM_DEBUG
	Turn debug messages on/off

	usage::

	  int debug;

	  ioctl(fd, CDROM_DEBUG, debug);

	inputs:
		Cdrom debug flag, 0=disable, 1=enable

	outputs:
		The ioctl return value will be the new debug flag.

	error return:
	  - EACCES	Access denied:  requires CAP_SYS_ADMIN

CDROM_GET_CAPABILITY
	get capabilities

	usage::

	  ioctl(fd, CDROM_GET_CAPABILITY, 0);

	inputs:
		none

	outputs:
		The ioctl return value is the current device capability
		flags.  See CDC_CLOSE_TRAY, CDC_OPEN_TRAY, etc.

CDROMAUDIOBUFSIZ
	set the audio buffer size

	usage::

	  int arg;

	  ioctl(fd, CDROMAUDIOBUFSIZ, val);

	inputs:
		New audio buffer size

	outputs:
		The ioctl return value is the new audio buffer size, or -1
		on error.

	error return:
	  - ENOSYS	Not supported by this driver.

	notes:
		Not supported by all drivers.

DVD_READ_STRUCT			Read structure

	usage::

	  dvd_struct s;

	  ioctl(fd, DVD_READ_STRUCT, &s);

	inputs:
		dvd_struct structure, containing:

	    =================== ==========================================
	    type		specifies the information desired, one of
				DVD_STRUCT_PHYSICAL, DVD_STRUCT_COPYRIGHT,
				DVD_STRUCT_DISCKEY, DVD_STRUCT_BCA,
				DVD_STRUCT_MANUFACT
	    physical.layer_num	desired layer, indexed from 0
	    copyright.layer_num	desired layer, indexed from 0
	    disckey.agid
	    =================== ==========================================

	outputs:
		dvd_struct structure, containing:

	    =================== ================================
	    physical		for type == DVD_STRUCT_PHYSICAL
	    copyright		for type == DVD_STRUCT_COPYRIGHT
	    disckey.value	for type == DVD_STRUCT_DISCKEY
	    bca.{len,value}	for type == DVD_STRUCT_BCA
	    manufact.{len,valu}	for type == DVD_STRUCT_MANUFACT
	    =================== ================================

	error returns:
	  - EINVAL	physical.layer_num exceeds number of layers
	  - EIO		Received invalid response from drive

DVD_WRITE_STRUCT		Write structure

	Not implemented, as of 2.6.8.1

DVD_AUTH			Authentication

	usage::

	  dvd_authinfo ai;

	  ioctl(fd, DVD_AUTH, &ai);

	inputs:
		dvd_authinfo structure.  See <linux/cdrom.h>

	outputs:
		dvd_authinfo structure.

	error return:
	  - ENOTTY	ai.type not recognized.

CDROM_SEND_PACKET
	send a packet to the drive

	usage::

	  struct cdrom_generic_command cgc;

	  ioctl(fd, CDROM_SEND_PACKET, &cgc);

	inputs:
		cdrom_generic_command structure containing the packet to send.

	outputs:
		none

	  cdrom_generic_command structure containing results.

	error return:
	  - EIO

			command failed.
	  - EPERM

			Operation not permitted, either because a
			write command was attempted on a drive which
			is opened read-only, or because the command
			requires CAP_SYS_RAWIO
	  - EINVAL

			cgc.data_direction not set

CDROM_NEXT_WRITABLE
	get next writable block

	usage::

	  long next;

	  ioctl(fd, CDROM_NEXT_WRITABLE, &next);

	inputs:
		none

	outputs:
		The next writable block.

	notes:
		If the device does not support this ioctl directly, the

	  ioctl will return CDROM_LAST_WRITTEN + 7.

CDROM_LAST_WRITTEN
	get last block written on disc

	usage::

	  long last;

	  ioctl(fd, CDROM_LAST_WRITTEN, &last);

	inputs:
		none

	outputs:
		The last block written on disc

	notes:
		If the device does not support this ioctl directly, the
		result is derived from the disc's table of contents.  If the
		table of contents can't be read, this ioctl returns an
		error.


code:
==============================
Summary of `HDIO_` ioctl calls
==============================

- Edward A. Falk <efalk@google.com>

November, 2004

This document attempts to describe the ioctl(2) calls supported by
the HD/IDE layer.  These are by-and-large implemented (as of Linux 5.11)
drivers/ata/libata-scsi.c.

ioctl values are listed in <linux/hdreg.h>.  As of this writing, they
are as follows:

    ioctls that pass argument pointers to user space:

	=======================	=======================================
	HDIO_GETGEO		get device geometry
	HDIO_GET_32BIT		get current io_32bit setting
	HDIO_GET_IDENTITY	get IDE identification info
	HDIO_DRIVE_TASKFILE	execute raw taskfile
	HDIO_DRIVE_TASK		execute task and special drive command
	HDIO_DRIVE_CMD		execute a special drive command
	=======================	=======================================

    ioctls that pass non-pointer values:

	=======================	=======================================
	HDIO_SET_32BIT		change io_32bit flags
	=======================	=======================================

The information that follows was determined from reading kernel source
code.  It is likely that some corrections will be made over time.

------------------------------------------------------------------------------

General:

	Unless otherwise specified, all ioctl calls return 0 on success
	and -1 with errno set to an appropriate value on error.

	Unless otherwise specified, all ioctl calls return -1 and set
	errno to EFAULT on a failed attempt to copy data to or from user
	address space.

	Unless otherwise specified, all data structures and constants
	are defined in <linux/hdreg.h>

------------------------------------------------------------------------------

HDIO_GETGEO
	get device geometry

	usage::

	  struct hd_geometry geom;

	  ioctl(fd, HDIO_GETGEO, &geom);

	inputs:
		none

	outputs:
		hd_geometry structure containing:

	    =========	==================================
	    heads	number of heads
	    sectors	number of sectors/track
	    cylinders	number of cylinders, mod 65536
	    start	starting sector of this partition.
	    =========	==================================

	error returns:
	  - EINVAL

			if the device is not a disk drive or floppy drive,
			or if the user passes a null pointer

	notes:
		Not particularly useful with modern disk drives, whose geometry
		is a polite fiction anyway.  Modern drives are addressed
		purely by sector number nowadays (lba addressing), and the
		drive geometry is an abstraction which is actually subject
		to change.  Currently (as of Nov 2004), the geometry values
		are the "bios" values -- presumably the values the drive had
		when Linux first booted.

		In addition, the cylinders field of the hd_geometry is an
		unsigned short, meaning that on most architectures, this
		ioctl will not return a meaningful value on drives with more
		than 65535 tracks.

		The start field is unsigned long, meaning that it will not
		contain a meaningful value for disks over 219 Gb in size.

HDIO_GET_IDENTITY
	get IDE identification info

	usage::

	  unsigned char identity[512];

	  ioctl(fd, HDIO_GET_IDENTITY, identity);

	inputs:
		none

	outputs:
		ATA drive identity information.  For full description, see
		the IDENTIFY DEVICE and IDENTIFY PACKET DEVICE commands in
		the ATA specification.

	error returns:
	  - EINVAL	Called on a partition instead of the whole disk device
	  - ENOMSG	IDENTIFY DEVICE information not available

	notes:
		Returns information that was obtained when the drive was
		probed.  Some of this information is subject to change, and
		this ioctl does not re-probe the drive to update the
		information.

		This information is also available from /proc/ide/hdX/identify

HDIO_GET_32BIT
	get current io_32bit setting

	usage::

	  long val;

	  ioctl(fd, HDIO_GET_32BIT, &val);

	inputs:
		none

	outputs:
		The value of the current io_32bit setting

	notes:
		0=16-bit, 1=32-bit, 2,3 = 32bit+sync

HDIO_DRIVE_TASKFILE
	execute raw taskfile

	Note:
		If you don't have a copy of the ANSI ATA specification
		handy, you should probably ignore this ioctl.

	- Execute an ATA disk command directly by writing the "taskfile"
	  registers of the drive.  Requires ADMIN and RAWIO access
	  privileges.

	usage::

	  struct {

	    ide_task_request_t req_task;
	    u8 outbuf[OUTPUT_SIZE];
	    u8 inbuf[INPUT_SIZE];
	  } task;
	  memset(&task.req_task, 0, sizeof(task.req_task));
	  task.req_task.out_size = sizeof(task.outbuf);
	  task.req_task.in_size = sizeof(task.inbuf);
	  ...
	  ioctl(fd, HDIO_DRIVE_TASKFILE, &task);
	  ...

	inputs:

	  (See below for details on memory area passed to ioctl.)

	  ============	===================================================
	  io_ports[8]	values to be written to taskfile registers
	  hob_ports[8]	high-order bytes, for extended commands.
	  out_flags	flags indicating which registers are valid
	  in_flags	flags indicating which registers should be returned
	  data_phase	see below
	  req_cmd	command type to be executed
	  out_size	size of output buffer
	  outbuf	buffer of data to be transmitted to disk
	  inbuf		buffer of data to be received from disk (see [1])
	  ============	===================================================

	outputs:

	  ===========	====================================================
	  io_ports[]	values returned in the taskfile registers
	  hob_ports[]	high-order bytes, for extended commands.
	  out_flags	flags indicating which registers are valid (see [2])
	  in_flags	flags indicating which registers should be returned
	  outbuf	buffer of data to be transmitted to disk (see [1])
	  inbuf		buffer of data to be received from disk
	  ===========	====================================================

	error returns:
	  - EACCES	CAP_SYS_ADMIN or CAP_SYS_RAWIO privilege not set.
	  - ENOMSG	Device is not a disk drive.
	  - ENOMEM	Unable to allocate memory for task
	  - EFAULT	req_cmd == TASKFILE_IN_OUT (not implemented as of 2.6.8)
	  - EPERM

			req_cmd == TASKFILE_MULTI_OUT and drive
			multi-count not yet set.
	  - EIO		Drive failed the command.

	notes:

	  [1] READ THE FOLLOWING NOTES *CAREFULLY*.  THIS IOCTL IS
	  FULL OF GOTCHAS.  Extreme caution should be used with using
	  this ioctl.  A mistake can easily corrupt data or hang the
	  system.

	  [2] Both the input and output buffers are copied from the
	  user and written back to the user, even when not used.

	  [3] If one or more bits are set in out_flags and in_flags is
	  zero, the following values are used for in_flags.all and
	  written back into in_flags on completion.

	   * IDE_TASKFILE_STD_IN_FLAGS | (IDE_HOB_STD_IN_FLAGS << 8)
	     if LBA48 addressing is enabled for the drive
	   * IDE_TASKFILE_STD_IN_FLAGS
	     if CHS/LBA28

	  The association between in_flags.all and each enable
	  bitfield flips depending on endianness; fortunately, TASKFILE
	  only uses inflags.b.data bit and ignores all other bits.
	  The end result is that, on any endian machines, it has no
	  effect other than modifying in_flags on completion.

	  [4] The default value of SELECT is (0xa0|DEV_bit|LBA_bit)
	  except for four drives per port chipsets.  For four drives
	  per port chipsets, it's (0xa0|DEV_bit|LBA_bit) for the first
	  pair and (0x80|DEV_bit|LBA_bit) for the second pair.

	  [5] The argument to the ioctl is a pointer to a region of
	  memory containing a ide_task_request_t structure, followed
	  by an optional buffer of data to be transmitted to the
	  drive, followed by an optional buffer to receive data from
	  the drive.

	  Command is passed to the disk drive via the ide_task_request_t
	  structure, which contains these fields:

	    ============	===============================================
	    io_ports[8]		values for the taskfile registers
	    hob_ports[8]	high-order bytes, for extended commands
	    out_flags		flags indicating which entries in the
				io_ports[] and hob_ports[] arrays
				contain valid values.  Type ide_reg_valid_t.
	    in_flags		flags indicating which entries in the
				io_ports[] and hob_ports[] arrays
				are expected to contain valid values
				on return.
	    data_phase		See below
	    req_cmd		Command type, see below
	    out_size		output (user->drive) buffer size, bytes
	    in_size		input (drive->user) buffer size, bytes
	    ============	===============================================

	  When out_flags is zero, the following registers are loaded.

	    ============	===============================================
	    HOB_FEATURE		If the drive supports LBA48
	    HOB_NSECTOR		If the drive supports LBA48
	    HOB_SECTOR		If the drive supports LBA48
	    HOB_LCYL		If the drive supports LBA48
	    HOB_HCYL		If the drive supports LBA48
	    FEATURE
	    NSECTOR
	    SECTOR
	    LCYL
	    HCYL
	    SELECT		First, masked with 0xE0 if LBA48, 0xEF
				otherwise; then, or'ed with the default
				value of SELECT.
	    ============	===============================================

	  If any bit in out_flags is set, the following registers are loaded.

	    ============	===============================================
	    HOB_DATA		If out_flags.b.data is set.  HOB_DATA will
				travel on DD8-DD15 on little endian machines
				and on DD0-DD7 on big endian machines.
	    DATA		If out_flags.b.data is set.  DATA will
				travel on DD0-DD7 on little endian machines
				and on DD8-DD15 on big endian machines.
	    HOB_NSECTOR		If out_flags.b.nsector_hob is set
	    HOB_SECTOR		If out_flags.b.sector_hob is set
	    HOB_LCYL		If out_flags.b.lcyl_hob is set
	    HOB_HCYL		If out_flags.b.hcyl_hob is set
	    FEATURE		If out_flags.b.feature is set
	    NSECTOR		If out_flags.b.nsector is set
	    SECTOR		If out_flags.b.sector is set
	    LCYL		If out_flags.b.lcyl is set
	    HCYL		If out_flags.b.hcyl is set
	    SELECT		Or'ed with the default value of SELECT and
				loaded regardless of out_flags.b.select.
	    ============	===============================================

	  Taskfile registers are read back from the drive into
	  {io|hob}_ports[] after the command completes iff one of the
	  following conditions is met; otherwise, the original values
	  will be written back, unchanged.

	    1. The drive fails the command (EIO).
	    2. One or more than one bits are set in out_flags.
	    3. The requested data_phase is TASKFILE_NO_DATA.

	    ============	===============================================
	    HOB_DATA		If in_flags.b.data is set.  It will contain
				DD8-DD15 on little endian machines and
				DD0-DD7 on big endian machines.
	    DATA		If in_flags.b.data is set.  It will contain
				DD0-DD7 on little endian machines and
				DD8-DD15 on big endian machines.
	    HOB_FEATURE		If the drive supports LBA48
	    HOB_NSECTOR		If the drive supports LBA48
	    HOB_SECTOR		If the drive supports LBA48
	    HOB_LCYL		If the drive supports LBA48
	    HOB_HCYL		If the drive supports LBA48
	    NSECTOR
	    SECTOR
	    LCYL
	    HCYL
	    ============	===============================================

	  The data_phase field describes the data transfer to be
	  performed.  Value is one of:

	    ===================        ========================================
	    TASKFILE_IN
	    TASKFILE_MULTI_IN
	    TASKFILE_OUT
	    TASKFILE_MULTI_OUT
	    TASKFILE_IN_OUT
	    TASKFILE_IN_DMA
	    TASKFILE_IN_DMAQ		== IN_DMA (queueing not supported)
	    TASKFILE_OUT_DMA
	    TASKFILE_OUT_DMAQ		== OUT_DMA (queueing not supported)
	    TASKFILE_P_IN		unimplemented
	    TASKFILE_P_IN_DMA		unimplemented
	    TASKFILE_P_IN_DMAQ		unimplemented
	    TASKFILE_P_OUT		unimplemented
	    TASKFILE_P_OUT_DMA		unimplemented
	    TASKFILE_P_OUT_DMAQ		unimplemented
	    ===================        ========================================

	  The req_cmd field classifies the command type.  It may be
	  one of:

	    ========================    =======================================
	    IDE_DRIVE_TASK_NO_DATA
	    IDE_DRIVE_TASK_SET_XFER	unimplemented
	    IDE_DRIVE_TASK_IN
	    IDE_DRIVE_TASK_OUT		unimplemented
	    IDE_DRIVE_TASK_RAW_WRITE
	    ========================    =======================================

	  [6] Do not access {in|out}_flags->all except for resetting
	  all the bits.  Always access individual bit fields.  ->all
	  value will flip depending on endianness.  For the same
	  reason, do not use IDE_{TASKFILE|HOB}_STD_{OUT|IN}_FLAGS
	  constants defined in hdreg.h.

HDIO_DRIVE_CMD
	execute a special drive command

	Note:  If you don't have a copy of the ANSI ATA specification
	handy, you should probably ignore this ioctl.

	usage::

	  u8 args[4+XFER_SIZE];

	  ...
	  ioctl(fd, HDIO_DRIVE_CMD, args);

	inputs:
	    Commands other than WIN_SMART:

	    =======     =======
	    args[0]	COMMAND
	    args[1]	NSECTOR
	    args[2]	FEATURE
	    args[3]	NSECTOR
	    =======     =======

	    WIN_SMART:

	    =======     =======
	    args[0]	COMMAND
	    args[1]	SECTOR
	    args[2]	FEATURE
	    args[3]	NSECTOR
	    =======     =======

	outputs:
		args[] buffer is filled with register values followed by any

	  data returned by the disk.

	    ========	====================================================
	    args[0]	status
	    args[1]	error
	    args[2]	NSECTOR
	    args[3]	undefined
	    args[4+]	NSECTOR * 512 bytes of data returned by the command.
	    ========	====================================================

	error returns:
	  - EACCES	Access denied:  requires CAP_SYS_RAWIO
	  - ENOMEM	Unable to allocate memory for task
	  - EIO		Drive reports error

	notes:

	  [1] For commands other than WIN_SMART, args[1] should equal
	  args[3].  SECTOR, LCYL and HCYL are undefined.  For
	  WIN_SMART, 0x4f and 0xc2 are loaded into LCYL and HCYL
	  respectively.  In both cases SELECT will contain the default
	  value for the drive.  Please refer to HDIO_DRIVE_TASKFILE
	  notes for the default value of SELECT.

	  [2] If NSECTOR value is greater than zero and the drive sets
	  DRQ when interrupting for the command, NSECTOR * 512 bytes
	  are read from the device into the area following NSECTOR.
	  In the above example, the area would be
	  args[4..4+XFER_SIZE].  16bit PIO is used regardless of
	  HDIO_SET_32BIT setting.

	  [3] If COMMAND == WIN_SETFEATURES && FEATURE == SETFEATURES_XFER
	  && NSECTOR >= XFER_SW_DMA_0 && the drive supports any DMA
	  mode, IDE driver will try to tune the transfer mode of the
	  drive accordingly.

HDIO_DRIVE_TASK
	execute task and special drive command

	Note:  If you don't have a copy of the ANSI ATA specification
	handy, you should probably ignore this ioctl.

	usage::

	  u8 args[7];

	  ...
	  ioctl(fd, HDIO_DRIVE_TASK, args);

	inputs:
	    Taskfile register values:

	    =======	=======
	    args[0]	COMMAND
	    args[1]	FEATURE
	    args[2]	NSECTOR
	    args[3]	SECTOR
	    args[4]	LCYL
	    args[5]	HCYL
	    args[6]	SELECT
	    =======	=======

	outputs:
	    Taskfile register values:

	    =======	=======
	    args[0]	status
	    args[1]	error
	    args[2]	NSECTOR
	    args[3]	SECTOR
	    args[4]	LCYL
	    args[5]	HCYL
	    args[6]	SELECT
	    =======	=======

	error returns:
	  - EACCES	Access denied:  requires CAP_SYS_RAWIO
	  - ENOMEM	Unable to allocate memory for task
	  - ENOMSG	Device is not a disk drive.
	  - EIO		Drive failed the command.

	notes:

	  [1] DEV bit (0x10) of SELECT register is ignored and the
	  appropriate value for the drive is used.  All other bits
	  are used unaltered.

HDIO_SET_32BIT
	change io_32bit flags

	usage::

	  int val;

	  ioctl(fd, HDIO_SET_32BIT, val);

	inputs:
		New value for io_32bit flag

	outputs:
		none

	error return:
	  - EINVAL	Called on a partition instead of the whole disk device
	  - EACCES	Access denied:  requires CAP_SYS_ADMIN
	  - EINVAL	value out of range [0 3]
	  - EBUSY	Controller busy

repo_name: angery/linux


code:
.. SPDX-License-Identifier: GPL-2.0

======
IOCTLs
======

.. toctree::
   :maxdepth: 1

   ioctl-number

   ioctl-decoding

   cdrom
   hdio


code:
==============================
Decoding an IOCTL Magic Number
==============================

To decode a hex IOCTL code:

Most architectures use this generic format, but check
include/ARCH/ioctl.h for specifics, e.g. powerpc
uses 3 bits to encode read/write and 13 bits for size.

 ====== ==================================
 bits   meaning
 ====== ==================================
 31-30	00 - no parameters: uses _IO macro
	10 - read: _IOR
	01 - write: _IOW
	11 - read/write: _IOWR

 29-16	size of arguments

 15-8	ascii character supposedly
	unique to each driver

 7-0	function #
 ====== ==================================

So for example 0x82187201 is a read with arg length of 0x218,
character 'r' function 1. Grepping the source reveals this is::

	#define VFAT_IOCTL_READDIR_BOTH         _IOR('r', 1, struct dirent [2])


code:
=============
Ioctl Numbers
=============

19 October 1999

Michael Elizabeth Chastain
<mec@shout.net>

If you are adding new ioctl's to the kernel, you should use the _IO
macros defined in <linux/ioctl.h>:

    ====== == ============================================
    _IO    an ioctl with no parameters
    _IOW   an ioctl with write parameters (copy_from_user)
    _IOR   an ioctl with read parameters  (copy_to_user)
    _IOWR  an ioctl with both write and read parameters.
    ====== == ============================================

'Write' and 'read' are from the user's point of view, just like the
system calls 'write' and 'read'.  For example, a SET_FOO ioctl would
be _IOW, although the kernel would actually read data from user space;
a GET_FOO ioctl would be _IOR, although the kernel would actually write
data to user space.

The first argument to _IO, _IOW, _IOR, or _IOWR is an identifying letter
or number from the table below.  Because of the large number of drivers,
many drivers share a partial letter with other drivers.

If you are writing a driver for a new device and need a letter, pick an
unused block with enough room for expansion: 32 to 256 ioctl commands.
You can register the block by patching this file and submitting the
patch to Linus Torvalds.  Or you can e-mail me at <mec@shout.net> and
I'll register one for you.

The second argument to _IO, _IOW, _IOR, or _IOWR is a sequence number
to distinguish ioctls from each other.  The third argument to _IOW,
_IOR, or _IOWR is the type of the data going into the kernel or coming
out of the kernel (e.g.  'int' or 'struct foo').  NOTE!  Do NOT use
sizeof(arg) as the third argument as this results in your ioctl thinking
it passes an argument of type size_t.

Some devices use their major number as the identifier; this is OK, as
long as it is unique.  Some devices are irregular and don't follow any
convention at all.

Following this convention is good because:

(1) Keeping the ioctl's globally unique helps error checking:
    if a program calls an ioctl on the wrong device, it will get an
    error rather than some unexpected behaviour.

(2) The 'strace' build procedure automatically finds ioctl numbers
    defined with _IO, _IOW, _IOR, or _IOWR.

(3) 'strace' can decode numbers back into useful names when the
    numbers are unique.

(4) People looking for ioctls can grep for them more easily when
    this convention is used to define the ioctl numbers.

(5) When following the convention, the driver code can use generic
    code to copy the parameters between user and kernel space.

This table lists ioctls visible from user land for Linux/x86.  It contains
most drivers up to 2.6.31, but I know I am missing some.  There has been
no attempt to list non-X86 architectures or ioctls from drivers/staging/.

====  =====  ======================================================= ================================================================
Code  Seq#    Include File                                           Comments
      (hex)
====  =====  ======================================================= ================================================================
0x00  00-1F  linux/fs.h                                              conflict!
0x00  00-1F  scsi/scsi_ioctl.h                                       conflict!
0x00  00-1F  linux/fb.h                                              conflict!
0x00  00-1F  linux/wavefront.h                                       conflict!
0x02  all    linux/fd.h
0x03  all    linux/hdreg.h
0x04  D2-DC  linux/umsdos_fs.h                                       Dead since 2.6.11, but don't reuse these.
0x06  all    linux/lp.h
0x09  all    linux/raid/md_u.h
0x10  00-0F  drivers/char/s390/vmcp.h
0x10  10-1F  arch/s390/include/uapi/sclp_ctl.h
0x10  20-2F  arch/s390/include/uapi/asm/hypfs.h
0x12  all    linux/fs.h
             linux/blkpg.h
0x1b  all                                                            InfiniBand Subsystem
                                                                     <http://infiniband.sourceforge.net/>
0x20  all    drivers/cdrom/cm206.h
0x22  all    scsi/sg.h
0x3E  00-0F  linux/counter.h                                         <mailto:linux-iio@vger.kernel.org>
'!'   00-1F  uapi/linux/seccomp.h
'#'   00-3F                                                          IEEE 1394 Subsystem
                                                                     Block for the entire subsystem
'$'   00-0F  linux/perf_counter.h, linux/perf_event.h
'%'   00-0F  include/uapi/linux/stm.h                                System Trace Module subsystem
                                                                     <mailto:alexander.shishkin@linux.intel.com>
'&'   00-07  drivers/firewire/nosy-user.h
'1'   00-1F  linux/timepps.h                                         PPS kit from Ulrich Windl
                                                                     <ftp://ftp.de.kernel.org/pub/linux/daemons/ntp/PPS/>
'2'   01-04  linux/i2o.h
'3'   00-0F  drivers/s390/char/raw3270.h                             conflict!
'3'   00-1F  linux/suspend_ioctls.h,                                 conflict!
             kernel/power/user.c
'8'   all                                                            SNP8023 advanced NIC card
                                                                     <mailto:mcr@solidum.com>
';'   64-7F  linux/vfio.h
';'   80-FF  linux/iommufd.h
'='   00-3f  uapi/linux/ptp_clock.h                                  <mailto:richardcochran@gmail.com>
'@'   00-0F  linux/radeonfb.h                                        conflict!
'@'   00-0F  drivers/video/aty/aty128fb.c                            conflict!
'A'   00-1F  linux/apm_bios.h                                        conflict!
'A'   00-0F  linux/agpgart.h,                                        conflict!
             drivers/char/agp/compat_ioctl.h
'A'   00-7F  sound/asound.h                                          conflict!
'B'   00-1F  linux/cciss_ioctl.h                                     conflict!
'B'   00-0F  include/linux/pmu.h                                     conflict!
'B'   C0-FF  advanced bbus                                           <mailto:maassen@uni-freiburg.de>
'B'   00-0F  xen/xenbus_dev.h                                        conflict!
'C'   all    linux/soundcard.h                                       conflict!
'C'   01-2F  linux/capi.h                                            conflict!
'C'   F0-FF  drivers/net/wan/cosa.h                                  conflict!
'D'   all    arch/s390/include/asm/dasd.h
'D'   40-5F  drivers/scsi/dpt/dtpi_ioctl.h                           Dead since 2022
'D'   05     drivers/scsi/pmcraid.h
'E'   all    linux/input.h                                           conflict!
'E'   00-0F  xen/evtchn.h                                            conflict!
'F'   all    linux/fb.h                                              conflict!
'F'   01-02  drivers/scsi/pmcraid.h                                  conflict!
'F'   20     drivers/video/fsl-diu-fb.h                              conflict!
'F'   20     drivers/video/intelfb/intelfb.h                         conflict!
'F'   20     linux/ivtvfb.h                                          conflict!
'F'   20     linux/matroxfb.h                                        conflict!
'F'   20     drivers/video/aty/atyfb_base.c                          conflict!
'F'   00-0F  video/da8xx-fb.h                                        conflict!
'F'   80-8F  linux/arcfb.h                                           conflict!
'F'   DD     video/sstfb.h                                           conflict!
'G'   00-3F  drivers/misc/sgi-gru/grulib.h                           conflict!
'G'   00-0F  xen/gntalloc.h, xen/gntdev.h                            conflict!
'H'   00-7F  linux/hiddev.h                                          conflict!
'H'   00-0F  linux/hidraw.h                                          conflict!
'H'   01     linux/mei.h                                             conflict!
'H'   02     linux/mei.h                                             conflict!
'H'   03     linux/mei.h                                             conflict!
'H'   00-0F  sound/asound.h                                          conflict!
'H'   20-40  sound/asound_fm.h                                       conflict!
'H'   80-8F  sound/sfnt_info.h                                       conflict!
'H'   10-8F  sound/emu10k1.h                                         conflict!
'H'   10-1F  sound/sb16_csp.h                                        conflict!
'H'   10-1F  sound/hda_hwdep.h                                       conflict!
'H'   40-4F  sound/hdspm.h                                           conflict!
'H'   40-4F  sound/hdsp.h                                            conflict!
'H'   90     sound/usb/usx2y/usb_stream.h
'H'   00-0F  uapi/misc/habanalabs.h                                  conflict!
'H'   A0     uapi/linux/usb/cdc-wdm.h
'H'   C0-F0  net/bluetooth/hci.h                                     conflict!
'H'   C0-DF  net/bluetooth/hidp/hidp.h                               conflict!
'H'   C0-DF  net/bluetooth/cmtp/cmtp.h                               conflict!
'H'   C0-DF  net/bluetooth/bnep/bnep.h                               conflict!
'H'   F1     linux/hid-roccat.h                                      <mailto:erazor_de@users.sourceforge.net>
'H'   F8-FA  sound/firewire.h
'I'   all    linux/isdn.h                                            conflict!
'I'   00-0F  drivers/isdn/divert/isdn_divert.h                       conflict!
'I'   40-4F  linux/mISDNif.h                                         conflict!
'K'   all    linux/kd.h
'L'   00-1F  linux/loop.h                                            conflict!
'L'   10-1F  drivers/scsi/mpt3sas/mpt3sas_ctl.h                      conflict!
'L'   E0-FF  linux/ppdd.h                                            encrypted disk device driver
                                                                     <http://linux01.gwdg.de/~alatham/ppdd.html>
'M'   all    linux/soundcard.h                                       conflict!
'M'   01-16  mtd/mtd-abi.h                                           conflict!
      and    drivers/mtd/mtdchar.c
'M'   01-03  drivers/scsi/megaraid/megaraid_sas.h
'M'   00-0F  drivers/video/fsl-diu-fb.h                              conflict!
'N'   00-1F  drivers/usb/scanner.h
'N'   40-7F  drivers/block/nvme.c
'O'   00-06  mtd/ubi-user.h                                          UBI
'P'   all    linux/soundcard.h                                       conflict!
'P'   60-6F  sound/sscape_ioctl.h                                    conflict!
'P'   00-0F  drivers/usb/class/usblp.c                               conflict!
'P'   01-09  drivers/misc/pci_endpoint_test.c                        conflict!
'P'   00-0F  xen/privcmd.h                                           conflict!
'Q'   all    linux/soundcard.h
'R'   00-1F  linux/random.h                                          conflict!
'R'   01     linux/rfkill.h                                          conflict!
'R'   C0-DF  net/bluetooth/rfcomm.h
'R'   E0     uapi/linux/fsl_mc.h
'S'   all    linux/cdrom.h                                           conflict!
'S'   80-81  scsi/scsi_ioctl.h                                       conflict!
'S'   82-FF  scsi/scsi.h                                             conflict!
'S'   00-7F  sound/asequencer.h                                      conflict!
'T'   all    linux/soundcard.h                                       conflict!
'T'   00-AF  sound/asound.h                                          conflict!
'T'   all    arch/x86/include/asm/ioctls.h                           conflict!
'T'   C0-DF  linux/if_tun.h                                          conflict!
'U'   all    sound/asound.h                                          conflict!
'U'   00-CF  linux/uinput.h                                          conflict!
'U'   00-EF  linux/usbdevice_fs.h
'U'   C0-CF  drivers/bluetooth/hci_uart.h
'V'   all    linux/vt.h                                              conflict!
'V'   all    linux/videodev2.h                                       conflict!
'V'   C0     linux/ivtvfb.h                                          conflict!
'V'   C0     linux/ivtv.h                                            conflict!
'V'   C0     media/davinci/vpfe_capture.h                            conflict!
'V'   C0     media/si4713.h                                          conflict!
'W'   00-1F  linux/watchdog.h                                        conflict!
'W'   00-1F  linux/wanrouter.h                                       conflict! (pre 3.9)
'W'   00-3F  sound/asound.h                                          conflict!
'W'   40-5F  drivers/pci/switch/switchtec.c
'W'   60-61  linux/watch_queue.h
'X'   all    fs/xfs/xfs_fs.h,                                        conflict!
             fs/xfs/linux-2.6/xfs_ioctl32.h,
             include/linux/falloc.h,
             linux/fs.h,
'X'   all    fs/ocfs2/ocfs_fs.h                                      conflict!
'X'   01     linux/pktcdvd.h                                         conflict!
'Z'   14-15  drivers/message/fusion/mptctl.h
'['   00-3F  linux/usb/tmc.h                                         USB Test and Measurement Devices
                                                                     <mailto:gregkh@linuxfoundation.org>
'a'   all    linux/atm*.h, linux/sonet.h                             ATM on linux
                                                                     <http://lrcwww.epfl.ch/>
'a'   00-0F  drivers/crypto/qat/qat_common/adf_cfg_common.h          conflict! qat driver
'b'   00-FF                                                          conflict! bit3 vme host bridge
                                                                     <mailto:natalia@nikhefk.nikhef.nl>
'c'   all    linux/cm4000_cs.h                                       conflict!
'c'   00-7F  linux/comstats.h                                        conflict!
'c'   00-7F  linux/coda.h                                            conflict!
'c'   00-1F  linux/chio.h                                            conflict!
'c'   80-9F  arch/s390/include/asm/chsc.h                            conflict!
'c'   A0-AF  arch/x86/include/asm/msr.h conflict!
'd'   00-FF  linux/char/drm/drm.h                                    conflict!
'd'   02-40  pcmcia/ds.h                                             conflict!
'd'   F0-FF  linux/digi1.h
'e'   all    linux/digi1.h                                           conflict!
'f'   00-1F  linux/ext2_fs.h                                         conflict!
'f'   00-1F  linux/ext3_fs.h                                         conflict!
'f'   00-0F  fs/jfs/jfs_dinode.h                                     conflict!
'f'   00-0F  fs/ext4/ext4.h                                          conflict!
'f'   00-0F  linux/fs.h                                              conflict!
'f'   00-0F  fs/ocfs2/ocfs2_fs.h                                     conflict!
'f'   13-27  linux/fscrypt.h
'f'   81-8F  linux/fsverity.h
'g'   00-0F  linux/usb/gadgetfs.h
'g'   20-2F  linux/usb/g_printer.h
'h'   00-7F                                                          conflict! Charon filesystem
                                                                     <mailto:zapman@interlan.net>
'h'   00-1F  linux/hpet.h                                            conflict!
'h'   80-8F  fs/hfsplus/ioctl.c
'i'   00-3F  linux/i2o-dev.h                                         conflict!
'i'   0B-1F  linux/ipmi.h                                            conflict!
'i'   80-8F  linux/i8k.h
'i'   90-9F  `linux/iio/*.h`                                         IIO
'j'   00-3F  linux/joystick.h
'k'   00-0F  linux/spi/spidev.h                                      conflict!
'k'   00-05  video/kyro.h                                            conflict!
'k'   10-17  linux/hsi/hsi_char.h                                    HSI character device
'l'   00-3F  linux/tcfs_fs.h                                         transparent cryptographic file system
                                                                     <http://web.archive.org/web/%2A/http://mikonos.dia.unisa.it/tcfs>
'l'   40-7F  linux/udf_fs_i.h                                        in development:
                                                                     <https://github.com/pali/udftools>
'm'   00-09  linux/mmtimer.h                                         conflict!
'm'   all    linux/mtio.h                                            conflict!
'm'   all    linux/soundcard.h                                       conflict!
'm'   all    linux/synclink.h                                        conflict!
'm'   00-19  drivers/message/fusion/mptctl.h                         conflict!
'm'   00     drivers/scsi/megaraid/megaraid_ioctl.h                  conflict!
'n'   00-7F  linux/ncp_fs.h and fs/ncpfs/ioctl.c
'n'   80-8F  uapi/linux/nilfs2_api.h                                 NILFS2
'n'   E0-FF  linux/matroxfb.h                                        matroxfb
'o'   00-1F  fs/ocfs2/ocfs2_fs.h                                     OCFS2
'o'   00-03  mtd/ubi-user.h                                          conflict! (OCFS2 and UBI overlaps)
'o'   40-41  mtd/ubi-user.h                                          UBI
'o'   01-A1  `linux/dvb/*.h`                                         DVB
'p'   00-0F  linux/phantom.h                                         conflict! (OpenHaptics needs this)
'p'   00-1F  linux/rtc.h                                             conflict!
'p'   40-7F  linux/nvram.h
'p'   80-9F  linux/ppdev.h                                           user-space parport
                                                                     <mailto:tim@cyberelk.net>
'p'   A1-A5  linux/pps.h                                             LinuxPPS
                                                                     <mailto:giometti@linux.it>
'q'   00-1F  linux/serio.h
'q'   80-FF  linux/telephony.h                                       Internet PhoneJACK, Internet LineJACK
             linux/ixjuser.h                                         <http://web.archive.org/web/%2A/http://www.quicknet.net>
'r'   00-1F  linux/msdos_fs.h and fs/fat/dir.c
's'   all    linux/cdk.h
't'   00-7F  linux/ppp-ioctl.h
't'   80-8F  linux/isdn_ppp.h
't'   90-91  linux/toshiba.h                                         toshiba and toshiba_acpi SMM
'u'   00-1F  linux/smb_fs.h                                          gone
'u'   20-3F  linux/uvcvideo.h                                        USB video class host driver
'u'   40-4f  linux/udmabuf.h                                         userspace dma-buf misc device
'v'   00-1F  linux/ext2_fs.h                                         conflict!
'v'   00-1F  linux/fs.h                                              conflict!
'v'   00-0F  linux/sonypi.h                                          conflict!
'v'   00-0F  media/v4l2-subdev.h                                     conflict!
'v'   20-27  arch/powerpc/include/uapi/asm/vas-api.h		     VAS API
'v'   C0-FF  linux/meye.h                                            conflict!
'w'   all                                                            CERN SCI driver
'y'   00-1F                                                          packet based user level communications
                                                                     <mailto:zapman@interlan.net>
'z'   00-3F                                                          CAN bus card conflict!
                                                                     <mailto:hdstich@connectu.ulm.circular.de>
'z'   40-7F                                                          CAN bus card conflict!
                                                                     <mailto:oe@port.de>
'z'   10-4F  drivers/s390/crypto/zcrypt_api.h                        conflict!
'|'   00-7F  linux/media.h
0x80  00-1F  linux/fb.h
0x81  00-1F  linux/vduse.h
0x89  00-06  arch/x86/include/asm/sockios.h
0x89  0B-DF  linux/sockios.h
0x89  E0-EF  linux/sockios.h                                         SIOCPROTOPRIVATE range
0x89  F0-FF  linux/sockios.h                                         SIOCDEVPRIVATE range
0x8B  all    linux/wireless.h
0x8C  00-3F                                                          WiNRADiO driver
                                                                     <http://www.winradio.com.au/>
0x90  00     drivers/cdrom/sbpcd.h
0x92  00-0F  drivers/usb/mon/mon_bin.c
0x93  60-7F  linux/auto_fs.h
0x94  all    fs/btrfs/ioctl.h                                        Btrfs filesystem
             and linux/fs.h                                          some lifted to vfs/generic
0x97  00-7F  fs/ceph/ioctl.h                                         Ceph file system
0x99  00-0F                                                          537-Addinboard driver
                                                                     <mailto:buk@buks.ipn.de>
0xA0  all    linux/sdp/sdp.h                                         Industrial Device Project
                                                                     <mailto:kenji@bitgate.com>
0xA1  0      linux/vtpm_proxy.h                                      TPM Emulator Proxy Driver
0xA2  all    uapi/linux/acrn.h                                       ACRN hypervisor
0xA3  80-8F                                                          Port ACL  in development:
                                                                     <mailto:tlewis@mindspring.com>
0xA3  90-9F  linux/dtlk.h
0xA4  00-1F  uapi/linux/tee.h                                        Generic TEE subsystem
0xA4  00-1F  uapi/asm/sgx.h                                          <mailto:linux-sgx@vger.kernel.org>
0xA5  01-05  linux/surface_aggregator/cdev.h                         Microsoft Surface Platform System Aggregator
                                                                     <mailto:luzmaximilian@gmail.com>
0xA5  20-2F  linux/surface_aggregator/dtx.h                          Microsoft Surface DTX driver
                                                                     <mailto:luzmaximilian@gmail.com>
0xAA  00-3F  linux/uapi/linux/userfaultfd.h
0xAB  00-1F  linux/nbd.h
0xAC  00-1F  linux/raw.h
0xAD  00                                                             Netfilter device in development:
                                                                     <mailto:rusty@rustcorp.com.au>
0xAE  00-1F  linux/kvm.h                                             Kernel-based Virtual Machine
                                                                     <mailto:kvm@vger.kernel.org>
0xAE  40-FF  linux/kvm.h                                             Kernel-based Virtual Machine
                                                                     <mailto:kvm@vger.kernel.org>
0xAE  20-3F  linux/nitro_enclaves.h                                  Nitro Enclaves
0xAF  00-1F  linux/fsl_hypervisor.h                                  Freescale hypervisor
0xB0  all                                                            RATIO devices in development:
                                                                     <mailto:vgo@ratio.de>
0xB1  00-1F                                                          PPPoX
                                                                     <mailto:mostrows@styx.uwaterloo.ca>
0xB3  00     linux/mmc/ioctl.h
0xB4  00-0F  linux/gpio.h                                            <mailto:linux-gpio@vger.kernel.org>
0xB5  00-0F  uapi/linux/rpmsg.h                                      <mailto:linux-remoteproc@vger.kernel.org>
0xB6  all    linux/fpga-dfl.h
0xB7  all    uapi/linux/remoteproc_cdev.h                            <mailto:linux-remoteproc@vger.kernel.org>
0xB7  all    uapi/linux/nsfs.h                                       <mailto:Andrei Vagin <avagin@openvz.org>>
0xC0  00-0F  linux/usb/iowarrior.h
0xCA  00-0F  uapi/misc/cxl.h
0xCA  10-2F  uapi/misc/ocxl.h
0xCA  80-BF  uapi/scsi/cxlflash_ioctl.h
0xCB  00-1F                                                          CBM serial IEC bus in development:
                                                                     <mailto:michael.klein@puffin.lb.shuttle.de>
0xCC  00-0F  drivers/misc/ibmvmc.h                                   pseries VMC driver
0xCD  01     linux/reiserfs_fs.h
0xCE  01-02  uapi/linux/cxl_mem.h                                    Compute Express Link Memory Devices
0xCF  02     fs/cifs/ioctl.c
0xDB  00-0F  drivers/char/mwave/mwavepub.h
0xDD  00-3F                                                          ZFCP device driver see drivers/s390/scsi/
                                                                     <mailto:aherrman@de.ibm.com>
0xE5  00-3F  linux/fuse.h
0xEC  00-01  drivers/platform/chrome/cros_ec_dev.h                   ChromeOS EC driver
0xEE  00-09  uapi/linux/pfrut.h                                      Platform Firmware Runtime Update and Telemetry
0xF3  00-3F  drivers/usb/misc/sisusbvga/sisusb.h                     sisfb (in development)
                                                                     <mailto:thomas@winischhofer.net>
0xF6  all                                                            LTTng Linux Trace Toolkit Next Generation
                                                                     <mailto:mathieu.desnoyers@efficios.com>
0xF8  all    arch/x86/include/uapi/asm/amd_hsmp.h                    AMD HSMP EPYC system management interface driver
                                                                     <mailto:nchatrad@amd.com>
0xFD  all    linux/dm-ioctl.h
0xFE  all    linux/isst_if.h
====  =====  ======================================================= ================================================================


code:
.. SPDX-License-Identifier: GPL-2.0
.. iommu:

=====================================
IOMMU Userspace API
=====================================

IOMMU UAPI is used for virtualization cases where communications are
needed between physical and virtual IOMMU drivers. For baremetal
usage, the IOMMU is a system device which does not need to communicate
with userspace directly.

The primary use cases are guest Shared Virtual Address (SVA) and
guest IO virtual address (IOVA), wherein the vIOMMU implementation
relies on the physical IOMMU and for this reason requires interactions
with the host driver.

.. contents:: :local:

Functionalities
===============
Communications of user and kernel involve both directions. The
supported user-kernel APIs are as follows:

1. Bind/Unbind guest PASID (e.g. Intel VT-d)
2. Bind/Unbind guest PASID table (e.g. ARM SMMU)
3. Invalidate IOMMU caches upon guest requests
4. Report errors to the guest and serve page requests

Requirements
============
The IOMMU UAPIs are generic and extensible to meet the following
requirements:

1. Emulated and para-virtualised vIOMMUs
2. Multiple vendors (Intel VT-d, ARM SMMU, etc.)
3. Extensions to the UAPI shall not break existing userspace

Interfaces
==========
Although the data structures defined in IOMMU UAPI are self-contained,
there are no user API functions introduced. Instead, IOMMU UAPI is
designed to work with existing user driver frameworks such as VFIO.

Extension Rules & Precautions
-----------------------------
When IOMMU UAPI gets extended, the data structures can *only* be
modified in two ways:

1. Adding new fields by re-purposing the padding[] field. No size change.
2. Adding new union members at the end. May increase the structure sizes.

No new fields can be added *after* the variable sized union in that it
will break backward compatibility when offset moves. A new flag must
be introduced whenever a change affects the structure using either
method. The IOMMU driver processes the data based on flags which
ensures backward compatibility.

Version field is only reserved for the unlikely event of UAPI upgrade
at its entirety.

It's *always* the caller's responsibility to indicate the size of the
structure passed by setting argsz appropriately.
Though at the same time, argsz is user provided data which is not
trusted. The argsz field allows the user app to indicate how much data
it is providing; it's still the kernel's responsibility to validate
whether it's correct and sufficient for the requested operation.

Compatibility Checking
----------------------
When IOMMU UAPI extension results in some structure size increase,
IOMMU UAPI code shall handle the following cases:

1. User and kernel has exact size match
2. An older user with older kernel header (smaller UAPI size) running on a
   newer kernel (larger UAPI size)
3. A newer user with newer kernel header (larger UAPI size) running
   on an older kernel.
4. A malicious/misbehaving user passing illegal/invalid size but within
   range. The data may contain garbage.

Feature Checking
----------------
While launching a guest with vIOMMU, it is strongly advised to check
the compatibility upfront, as some subsequent errors happening during
vIOMMU operation, such as cache invalidation failures cannot be nicely
escalated to the guest due to IOMMU specifications. This can lead to
catastrophic failures for the users.

User applications such as QEMU are expected to import kernel UAPI
headers. Backward compatibility is supported per feature flags.
For example, an older QEMU (with older kernel header) can run on newer
kernel. Newer QEMU (with new kernel header) may refuse to initialize
on an older kernel if new feature flags are not supported by older
kernel. Simply recompiling existing code with newer kernel header should
not be an issue in that only existing flags are used.

IOMMU vendor driver should report the below features to IOMMU UAPI
consumers (e.g. via VFIO).

1. IOMMU_NESTING_FEAT_SYSWIDE_PASID
2. IOMMU_NESTING_FEAT_BIND_PGTBL
3. IOMMU_NESTING_FEAT_BIND_PASID_TABLE
4. IOMMU_NESTING_FEAT_CACHE_INVLD
5. IOMMU_NESTING_FEAT_PAGE_REQUEST

Take VFIO as example, upon request from VFIO userspace (e.g. QEMU),
VFIO kernel code shall query IOMMU vendor driver for the support of
the above features. Query result can then be reported back to the
userspace caller. Details can be found in
Documentation/driver-api/vfio.rst.

Data Passing Example with VFIO
------------------------------
As the ubiquitous userspace driver framework, VFIO is already IOMMU
aware and shares many key concepts such as device model, group, and
protection domain. Other user driver frameworks can also be extended
to support IOMMU UAPI but it is outside the scope of this document.

In this tight-knit VFIO-IOMMU interface, the ultimate consumer of the
IOMMU UAPI data is the host IOMMU driver. VFIO facilitates user-kernel
transport, capability checking, security, and life cycle management of
process address space ID (PASID).

VFIO layer conveys the data structures down to the IOMMU driver. It
follows the pattern below::

   struct {
	__u32 argsz;
	__u32 flags;
	__u8  data[];
   };

Here data[] contains the IOMMU UAPI data structures. VFIO has the
freedom to bundle the data as well as parse data size based on its own flags.

In order to determine the size and feature set of the user data, argsz
and flags (or the equivalent) are also embedded in the IOMMU UAPI data
structures.

A "__u32 argsz" field is *always* at the beginning of each structure.

For example:
::

   struct iommu_cache_invalidate_info {
	__u32	argsz;
	#define IOMMU_CACHE_INVALIDATE_INFO_VERSION_1 1
	__u32	version;
	/* IOMMU paging structure cache */
	#define IOMMU_CACHE_INV_TYPE_IOTLB	(1 << 0) /* IOMMU IOTLB */
	#define IOMMU_CACHE_INV_TYPE_DEV_IOTLB	(1 << 1) /* Device IOTLB */
	#define IOMMU_CACHE_INV_TYPE_PASID	(1 << 2) /* PASID cache */
	#define IOMMU_CACHE_INV_TYPE_NR		(3)
	__u8	cache;
	__u8	granularity;
	__u8	padding[6];
	union {
		struct iommu_inv_pasid_info pasid_info;
		struct iommu_inv_addr_info addr_info;
	} granu;
   };

VFIO is responsible for checking its own argsz and flags. It then
invokes appropriate IOMMU UAPI functions. The user pointers are passed
to the IOMMU layer for further processing. The responsibilities are
divided as follows:

- Generic IOMMU layer checks argsz range based on UAPI data in the
  current kernel version.

- Generic IOMMU layer checks content of the UAPI data for non-zero
  reserved bits in flags, padding fields, and unsupported version.
  This is to ensure not breaking userspace in the future when these
  fields or flags are used.

- Vendor IOMMU driver checks argsz based on vendor flags. UAPI data
  is consumed based on flags. Vendor driver has access to
  unadulterated argsz value in case of vendor specific future
  extensions. Currently, it does not perform the copy_from_user()
  itself. A __user pointer can be provided in some future scenarios
  where there's vendor data outside of the structure definition.

IOMMU code treats UAPI data in two categories:

- structure contains vendor data
  (Example: iommu_uapi_cache_invalidate())

- structure contains only generic data
  (Example: iommu_uapi_sva_bind_gpasid())

Sharing UAPI with in-kernel users
---------------------------------
For UAPIs that are shared with in-kernel users, a wrapper function is
provided to distinguish the callers. For example,

Userspace caller ::

  int iommu_uapi_sva_unbind_gpasid(struct iommu_domain *domain,
                                   struct device *dev,
                                   void __user *udata)

In-kernel caller ::

  int iommu_sva_unbind_gpasid(struct iommu_domain *domain,
                              struct device *dev, ioasid_t ioasid);


code:
.. SPDX-License-Identifier: GPL-2.0+

=======
IOMMUFD
=======

:Author: Jason Gunthorpe
:Author: Kevin Tian

Overview
========

IOMMUFD is the user API to control the IOMMU subsystem as it relates to managing
IO page tables from userspace using file descriptors. It intends to be general
and consumable by any driver that wants to expose DMA to userspace. These
drivers are eventually expected to deprecate any internal IOMMU logic
they may already/historically implement (e.g. vfio_iommu_type1.c).

At minimum iommufd provides universal support of managing I/O address spaces and
I/O page tables for all IOMMUs, with room in the design to add non-generic
features to cater to specific hardware functionality.

In this context the capital letter (IOMMUFD) refers to the subsystem while the
small letter (iommufd) refers to the file descriptors created via /dev/iommu for
use by userspace.

Key Concepts
============

User Visible Objects
--------------------

Following IOMMUFD objects are exposed to userspace:

- IOMMUFD_OBJ_IOAS, representing an I/O address space (IOAS), allowing map/unmap
  of user space memory into ranges of I/O Virtual Address (IOVA).

  The IOAS is a functional replacement for the VFIO container, and like the VFIO
  container it copies an IOVA map to a list of iommu_domains held within it.

- IOMMUFD_OBJ_DEVICE, representing a device that is bound to iommufd by an
  external driver.

- IOMMUFD_OBJ_HW_PAGETABLE, representing an actual hardware I/O page table
  (i.e. a single struct iommu_domain) managed by the iommu driver.

  The IOAS has a list of HW_PAGETABLES that share the same IOVA mapping and
  it will synchronize its mapping with each member HW_PAGETABLE.

All user-visible objects are destroyed via the IOMMU_DESTROY uAPI.

The diagram below shows relationship between user-visible objects and kernel
datastructures (external to iommufd), with numbers referred to operations
creating the objects and links::

  _________________________________________________________
 |                         iommufd                         |
 |       [1]                                               |
 |  _________________                                      |
 | |                 |                                     |
 | |                 |                                     |
 | |                 |                                     |
 | |                 |                                     |
 | |                 |                                     |
 | |                 |                                     |
 | |                 |        [3]                 [2]      |
 | |                 |    ____________         __________  |
 | |      IOAS       |<--|            |<------|          | |
 | |                 |   |HW_PAGETABLE|       |  DEVICE  | |
 | |                 |   |____________|       |__________| |
 | |                 |         |                   |       |
 | |                 |         |                   |       |
 | |                 |         |                   |       |
 | |                 |         |                   |       |
 | |                 |         |                   |       |
 | |_________________|         |                   |       |
 |         |                   |                   |       |
 |_________|___________________|___________________|_______|
           |                   |                   |
           |              _____v______      _______v_____
           | PFN storage |            |    |             |
           |------------>|iommu_domain|    |struct device|
                         |____________|    |_____________|

1. IOMMUFD_OBJ_IOAS is created via the IOMMU_IOAS_ALLOC uAPI. An iommufd can
   hold multiple IOAS objects. IOAS is the most generic object and does not
   expose interfaces that are specific to single IOMMU drivers. All operations
   on the IOAS must operate equally on each of the iommu_domains inside of it.

2. IOMMUFD_OBJ_DEVICE is created when an external driver calls the IOMMUFD kAPI
   to bind a device to an iommufd. The driver is expected to implement a set of
   ioctls to allow userspace to initiate the binding operation. Successful
   completion of this operation establishes the desired DMA ownership over the
   device. The driver must also set the driver_managed_dma flag and must not
   touch the device until this operation succeeds.

3. IOMMUFD_OBJ_HW_PAGETABLE is created when an external driver calls the IOMMUFD
   kAPI to attach a bound device to an IOAS. Similarly the external driver uAPI
   allows userspace to initiate the attaching operation. If a compatible
   pagetable already exists then it is reused for the attachment. Otherwise a
   new pagetable object and iommu_domain is created. Successful completion of
   this operation sets up the linkages among IOAS, device and iommu_domain. Once
   this completes the device could do DMA.

   Every iommu_domain inside the IOAS is also represented to userspace as a
   HW_PAGETABLE object.

   .. note::

      Future IOMMUFD updates will provide an API to create and manipulate the
      HW_PAGETABLE directly.

A device can only bind to an iommufd due to DMA ownership claim and attach to at
most one IOAS object (no support of PASID yet).

Kernel Datastructure
--------------------

User visible objects are backed by following datastructures:

- iommufd_ioas for IOMMUFD_OBJ_IOAS.
- iommufd_device for IOMMUFD_OBJ_DEVICE.
- iommufd_hw_pagetable for IOMMUFD_OBJ_HW_PAGETABLE.

Several terminologies when looking at these datastructures:

- Automatic domain - refers to an iommu domain created automatically when
  attaching a device to an IOAS object. This is compatible to the semantics of
  VFIO type1.

- Manual domain - refers to an iommu domain designated by the user as the
  target pagetable to be attached to by a device. Though currently there are
  no uAPIs to directly create such domain, the datastructure and algorithms
  are ready for handling that use case.

- In-kernel user - refers to something like a VFIO mdev that is using the
  IOMMUFD access interface to access the IOAS. This starts by creating an
  iommufd_access object that is similar to the domain binding a physical device
  would do. The access object will then allow converting IOVA ranges into struct
  page * lists, or doing direct read/write to an IOVA.

iommufd_ioas serves as the metadata datastructure to manage how IOVA ranges are
mapped to memory pages, composed of:

- struct io_pagetable holding the IOVA map
- struct iopt_area's representing populated portions of IOVA
- struct iopt_pages representing the storage of PFNs
- struct iommu_domain representing the IO page table in the IOMMU
- struct iopt_pages_access representing in-kernel users of PFNs
- struct xarray pinned_pfns holding a list of pages pinned by in-kernel users

Each iopt_pages represents a logical linear array of full PFNs. The PFNs are
ultimately derived from userspace VAs via an mm_struct. Once they have been
pinned the PFNs are stored in IOPTEs of an iommu_domain or inside the pinned_pfns
xarray if they have been pinned through an iommufd_access.

PFN have to be copied between all combinations of storage locations, depending
on what domains are present and what kinds of in-kernel "software access" users
exist. The mechanism ensures that a page is pinned only once.

An io_pagetable is composed of iopt_areas pointing at iopt_pages, along with a
list of iommu_domains that mirror the IOVA to PFN map.

Multiple io_pagetable-s, through their iopt_area-s, can share a single
iopt_pages which avoids multi-pinning and double accounting of page
consumption.

iommufd_ioas is sharable between subsystems, e.g. VFIO and VDPA, as long as
devices managed by different subsystems are bound to a same iommufd.

IOMMUFD User API
================

.. kernel-doc:: include/uapi/linux/iommufd.h

IOMMUFD Kernel API
==================

The IOMMUFD kAPI is device-centric with group-related tricks managed behind the
scene. This allows the external drivers calling such kAPI to implement a simple
device-centric uAPI for connecting its device to an iommufd, instead of
explicitly imposing the group semantics in its uAPI as VFIO does.

.. kernel-doc:: drivers/iommu/iommufd/device.c
   :export:

.. kernel-doc:: drivers/iommu/iommufd/main.c
   :export:

VFIO and IOMMUFD
----------------

Connecting a VFIO device to iommufd can be done in two ways.

First is a VFIO compatible way by directly implementing the /dev/vfio/vfio
container IOCTLs by mapping them into io_pagetable operations. Doing so allows
the use of iommufd in legacy VFIO applications by symlinking /dev/vfio/vfio to
/dev/iommufd or extending VFIO to SET_CONTAINER using an iommufd instead of a
container fd.

The second approach directly extends VFIO to support a new set of device-centric
user API based on aforementioned IOMMUFD kernel API. It requires userspace
change but better matches the IOMMUFD API semantics and easier to support new
iommufd features when comparing it to the first approach.

Currently both approaches are still work-in-progress.

There are still a few gaps to be resolved to catch up with VFIO type1, as
documented in iommufd_vfio_check_extension().

Future TODOs
============

Currently IOMMUFD supports only kernel-managed I/O page table, similar to VFIO
type1. New features on the radar include:

 - Binding iommu_domain's to PASID/SSID
 - Userspace page tables, for ARM, x86 and S390
 - Kernel bypass'd invalidation of user page tables
 - Re-use of the KVM page table in the IOMMU
 - Dirty page tracking in the IOMMU
 - Runtime Increase/Decrease of IOPTE size
 - PRI support with faults resolved in userspace