Linux I/O Ports and Hardware Address Ranges
Learn how Linux identifies hardware I/O ports, understand port-mapped I/O and MMIO, inspect /proc/ioports, and troubleshoot resource conflicts.
I/O means input/output communication between the processor, operating system, and physical hardware. A device such as a keyboard controller, serial adapter, storage controller, or display adapter exposes registers that the CPU and its driver use to send commands, read status, and exchange data.
An I/O port, also called an I/O address, is a numbered hardware address or address range used to access those device registers. In this lesson, a port such as 0x3F8 is a hardware address—not a network service endpoint such as TCP port 22.
This topic assumes basic command-line navigation, hexadecimal numbers, CPUs, RAM, device drivers, and Linux virtual filesystems such as procfs. For related command-line fundamentals, see showing the full path of shell commands and the Linux file structure.
What an I/O Port Is
A device register is a small control, status, or data location provided by hardware. A driver reads from and writes to these registers. An I/O port address tells the processor which register location the operation targets.
A device usually owns a range rather than one address. For example, a legacy serial UART might use 0x3F8-0x3FF. The first address can select one register, while subsequent addresses select other UART registers for data, status, configuration, or control.
I/O Address Spaces
Port-mapped I/O
Traditional PC-compatible systems provide a separate port-mapped I/O address space. Special processor instructions access this space instead of ordinary RAM. The operating system reserves ranges in this space and gives drivers permission to use the ranges belonging to their devices.
Port-mapped I/O is the traditional meaning of an I/O port on PC hardware. The address is not a location in normal system memory, even though the CPU uses an address value to select it.
Memory-mapped I/O
Many devices instead use memory-mapped I/O (MMIO). With MMIO, device registers occupy addresses in the physical memory address map. A driver accesses those addresses using memory operations, while the hardware interprets the accesses as register operations rather than RAM reads or writes.
PCI and PCIe devices commonly use MMIO regions described by their PCI BARs. A PCI BAR, or Base Address Register, identifies an assigned I/O or memory resource range for a device.
Hardware and firmware enumerate devices and their required resource ranges. The firmware and Linux kernel then assign resources, make them available to the appropriate drivers, and track ownership. The result may include port-mapped ranges, MMIO ranges, IRQs, and DMA channels.
Allocation, Ownership, and Conflicts
An assigned I/O range normally belongs to one device or driver at a time. Exclusive ownership prevents two unrelated drivers from writing to the same device registers or interpreting the same hardware responses.
A resource conflict occurs when two devices or drivers attempt to claim overlapping ranges, or when a device is configured to use a range already reserved by another device. Such a conflict can cause a driver probe to fail, prevent device initialization, produce unreliable behavior, or make both devices unusable.
Modern Plug and Play firmware and the Linux kernel usually assign resources automatically. Older hardware may have fixed addresses or configurable settings selected by jumpers, switches, firmware options, or driver parameters. Manual changes to legacy settings should be made only with the hardware documentation and driver requirements available.
Viewing Current I/O Assignments with /proc/ioports
/proc/ioports is a kernel-provided procfs file containing I/O port ranges currently reserved by the kernel and device drivers. Display it with:
cat /proc/ioports
Output varies by machine, kernel, firmware, enabled hardware, and drivers. A simplified example might look like this:
0000-001f : dma1
0020-003f : pic1
0060-0064 : i8042
0060-0060 : i8042
01f0-01f7 : ata_piix
01f0-01f7 : ata_piix
03f8-03ff : serial
Read each line as a hexadecimal start address, a hexadecimal end address, and an owner label. For example, 03f8-03ff : serial describes the range from 0x3F8 through 0x3FF, inclusive, reserved for a serial subsystem.
Indented lines show a hierarchical relationship. A broad reservation can contain a more specific child reservation, such as a bus, controller, or driver using part of a parent range. Names may refer to bridges, chipsets, PCI or ISA regions, kernel subsystems, or individual drivers.
Filter the listing when investigating familiar device categories:
grep -iE 'serial|ata|ide|vga|keyboard' /proc/ioports
Access to detailed hardware information can depend on system permissions, the kernel configuration, and which drivers are loaded. An empty or limited result does not necessarily mean that no hardware exists.
Common Legacy PC I/O Port Ranges
The following are conventional or historical PC assignments. They are reference points, not guarantees. A modern computer may omit the corresponding device, assign it differently, virtualize it, or use MMIO instead.
| Hexadecimal I/O range | Conventional device or function | Notes |
|---|---|---|
0x060-0x064 | Keyboard controller | Common legacy keyboard-controller registers; exact subranges and presentation vary. |
0x070-0x071 | CMOS and real-time clock | Traditionally used to select and access CMOS/RTC data. |
0x1F0-0x1F7 and 0x3F6 | Primary IDE/ATA controller | Legacy primary ATA command and control registers. |
0x170-0x177 and 0x376 | Secondary IDE/ATA controller | Legacy secondary ATA command and control registers. |
0x2F8-0x2FF | Legacy serial port COM2 | Conventional UART range; may be absent or reassigned. |
0x378-0x37F | Legacy parallel port LPT1 | Traditional printer/parallel-port range. |
0x3B0-0x3DF | VGA display adapter registers | Several VGA-related register ranges are traditionally located here. |
0x3E8-0x3EF | Legacy serial port COM3 | Conventional UART range; often absent on current systems. |
0x3F8-0x3FF | Legacy serial port COM1 | Common example of a legacy serial UART range. |
0x3F0-0x3F7 | Floppy disk controller | Historical floppy-controller range; may overlap conventionally listed ranges depending on controller use. |
For example, if a system has a legacy COM1-compatible UART, 0x3F8-0x3FF may appear in /proc/ioports. If the system has no legacy serial controller, the range may be absent or represented through a different current driver arrangement.
I/O Ports, IRQs, and DMA
I/O port addresses identify where the CPU reads and writes device registers. They are only one category of hardware resource.
- I/O port range: identifies register addresses in the port-mapped I/O space.
- IRQ: an Interrupt Request signal that tells the CPU that a device needs attention, such as when input arrives or an operation completes.
- DMA: Direct Memory Access, a mechanism that lets a device transfer data to or from RAM with reduced continuous CPU copying.
A device may use one, two, or all three. For example, a controller can expose control registers through I/O ports or MMIO, use an IRQ to report completion, and use DMA to move a large data buffer.
| Resource type | Purpose | Typical Linux inspection source | Example |
|---|---|---|---|
| I/O port range | Access device registers in the port-mapped I/O space. | /proc/ioports | 0x3F8-0x3FF for a conventional serial UART. |
| Memory-mapped I/O range | Access device registers placed in the physical memory address map. | /proc/iomem, lspci -vv | A PCIe device's assigned MMIO BAR. |
| IRQ | Notify the CPU that a device requires service. | /proc/interrupts, kernel messages | A controller interrupt after completing a transfer. |
| DMA channel | Transfer data between a device and RAM with less CPU copying. | Driver information and kernel messages | A legacy controller's assigned DMA channel. |
Investigating Modern Hardware
Legacy ranges are useful for understanding PC hardware, but they do not describe every device in a current Linux system. PCI and PCIe devices commonly use MMIO BARs. Laptops may omit serial, parallel, floppy, and IDE controllers entirely. USB devices are normally discovered through USB host controllers, and virtual machines may present synthetic or virtualized hardware resources.
Use these complementary commands:
cat /proc/iomem
lspci -vv
dmesg | grep -iE 'resource|ioport|conflict'
/proc/iomem shows physical memory address ranges, including regions reserved for MMIO devices. lspci -vv displays PCI devices and detailed assigned resource regions, including BAR information. The dmesg command searches kernel messages for resource reservation, device probing, and conflict reports.
Practical Investigation: A Missing Legacy Range
Suppose you expect to see the conventional COM1 range 0x3F8-0x3FF, but it is not present in /proc/ioports. Several explanations are possible:
- The machine may not contain a legacy serial controller.
- The device may use MMIO rather than port-mapped I/O.
- Firmware or virtualization may expose the device differently.
- The relevant kernel driver may not be loaded or may not reserve the range in the expected form.
Continue with:
lspci -vv
cat /proc/iomem
dmesg | grep -iE 'resource|ioport|conflict'
These checks distinguish an absent device from a device using MMIO or a different resource representation.
Practical Investigation: A Resource Conflict
If a driver reports that it cannot reserve an I/O region, another driver or device may already own the requested range. A legacy hardware address setting may also overlap a range assigned by firmware or Linux.
- Identify the hexadecimal range named in the driver or kernel message.
- Search for that range in
/proc/ioports. - Read the owner and any indented child entries.
- Check kernel messages for the driver that reserved the range.
- Compare the result with the hardware configuration and driver documentation before making changes.
cat /proc/ioports
dmesg | grep -iE 'resource|ioport|conflict'
Exam-Relevant Summary
- An I/O port, or I/O address, is a hardware resource address used to access device registers.
- Traditional port-mapped I/O uses a separate processor address space; MMIO places device registers in the physical memory address map.
/proc/ioportslists I/O port ranges currently reserved by the Linux kernel and drivers.- Ranges are normally exclusive because overlapping device register addresses can create resource conflicts.
- IRQ identifies how a device requests CPU attention; DMA identifies how it can transfer data to or from RAM.
- Modern PCI and PCIe hardware commonly uses MMIO BARs, so
/proc/iomemandlspci -vvcomplement/proc/ioports. - Legacy values such as
0x3F8-0x3FFfor COM1 are conventions, not universal assignments.
For broader hardware investigation, combine /proc/ioports, /proc/iomem, lspci -vv, and kernel messages rather than assuming that a familiar legacy range must exist on every system.