---
name: "io"
section: "4"
platform: "freebsd"
package: "base"
version: "16.0-CURRENT-20261027-61c70dc16a79"
release: "FreeBSD 16.0-CURRENT (2026-10-27)"
platforms: ["linux", "freebsd", "openbsd"]
url: "https://new.carta.tech/io/4"
---

# io(4)

I/O privilege file

## NAME

**io** – I/O privilege file

## SYNOPSIS

**device io**

**#include \<sys/types.h\>**

**#include \<sys/ioctl.h\>**

**#include \<dev/io/iodev.h\>**

**#include \<machine/iodev.h\>**

```
struct iodev_pio_req {
	u_int access;
	u_int port;
	u_int width;
	u_int val;
};
```

## DESCRIPTION

The special file */dev/io* is a controlled security hole that allows a process to gain I/O privileges (which are normally reserved for kernel-internal code). This can be useful in order to write userland programs that handle some hardware directly.

The usual operations on the device are to open it via the [open(2)](/open/2.md) interface and to send I/O requests to the file descriptor using the [ioctl(2)](/ioctl/2.md) syscall.

The [ioctl(2)](/ioctl/2.md) requests available for */dev/io* are mostly platform dependent, but there are also some in common between all of them. The IODEV\_PIO is used by all the architectures in order to request that an I/O operation be performed. It takes a 'struct iodev\_pio\_req' argument that must be previously setup.

The *access* member specifies the type of operation requested. It may be:

- IODEV\_PIO\_READ

  The operation is an "in" type. A value will be read from the specified port (retrieved from the *port* member) and the result will be stored in the *val* member.

- IODEV\_PIO\_WRITE

  The operation is a "out" type. The value will be fetched from the *val* member and will be written out to the specified port (defined as the *port* member).

Finally, the *width* member specifies the size of the operand to be read/written, expressed in bytes.

In addition to any file access permissions on */dev/io*, the kernel enforces that only the super-user may open this device.

## LEGACY

The */dev/io* interface used to be very i386 specific and worked differently. The initial implementation simply raised the *IOPL* of the current thread when [open(2)](/open/2.md) was called on the device. This behaviour is retained in the current implementation as legacy support for both i386 and amd64 architectures.

## SEE ALSO

[close(2)](/close/2.md), [i386\_get\_ioperm(2)](/i386_get_ioperm/2.md), [i386\_set\_ioperm(2)](/i386_set_ioperm/2.md), [ioctl(2)](/ioctl/2.md), [open(2)](/open/2.md), [mem(4)](/mem/4.md)

## HISTORY

The **io** file appeared in FreeBSD 1.0.
