← Previous Lecture Next Lecture →
If you are exploring port mapped I/O in Linux kernel programming for the first time, this guide is written for you. Port mapped I/O, usually shortened to PMIO or PIO, is one of the two classic ways a Linux device driver talks to hardware registers — the other being memory-mapped I/O (MMIO). This lecture is part of our free Linux kernel programming course and our broader free Linux device drivers course, and it explains port mapped I/O from the ground up, using only current, kernel 6.x accurate information.
What You Will Learn
- What port mapped I/O actually means and how it differs from memory-mapped I/O
- Why the x86 family still keeps a separate I/O port address space
- How the kernel tracks and protects I/O port regions
- How to inspect port usage on a running Linux system
- Where PMIO fits (and doesn’t fit) in modern, ARM-heavy embedded systems
Prerequisites
Before this lecture, you should be comfortable with:
- Basic C programming
- The idea of a kernel module (insmod / rmmod)
- Our earlier lecture on memory-mapped I/O (linked at the top of this page once published)
What Is Port Mapped I/O?
Every peripheral chip exposes a handful of registers — small memory cells that hold status bits, control bits, or data. The processor needs a way to reach those registers. There are exactly two strategies in general use, and port mapped I/O in the Linux kernel is one of them.
With MMIO, register addresses are folded into the same address space as normal RAM, so ordinary load/store instructions work. With PMIO, the processor instead has a completely separate address space reserved just for hardware registers, reached only through dedicated CPU instructions. A register accessed this way is called a “port,” and it has nothing to do with a network port — it is simply a hardware-facing address slot.
Port Mapped I/O on the x86 Architecture
Among mainstream CPU families, x86 is the one that still ships a genuine, hardware-backed port address space alongside MMIO. That space spans a 64-kilobyte range and historically hosts registers for legacy peripherals such as timers, DMA controllers, and the real-time clock. Most other architectures common in embedded Linux work — ARM, ARM64, RISC-V — have no separate port space at all; on those platforms, “port I/O” calls are implemented purely as thin wrappers around ordinary memory-mapped accesses. This is an important, current detail: if you are building drivers for a modern ARM SoC, true port mapped I/O simply does not exist in hardware, and the kernel quietly maps the API onto MMIO for source compatibility.
How the Kernel Protects I/O Port Regions
Because two drivers accidentally touching the same port range can crash a machine or corrupt hardware state, the kernel requires every driver to formally reserve its ports before use, and to release them on unload. This reservation is a bookkeeping step only — it does not perform any hardware I/O by itself, it simply prevents a second driver from claiming an overlapping range. Modern drivers typically prefer the “managed” variant tied to the driver’s device lifetime, so the reservation is automatically released even if the driver forgets to clean up on an error path — a robustness improvement widely adopted across the 5.x and 6.x driver tree.
| Aspect | MMIO | Port Mapped I/O (PMIO) |
|---|---|---|
| Address space | Shared with RAM | Separate, dedicated range |
| CPU instructions | Normal load/store | Dedicated I/O instructions |
| Register width options | 8 / 16 / 32 / 64-bit | 8 / 16 / 32-bit |
| Architecture support | Universal | Mainly x86; emulated elsewhere |
| Typical modern usage | Most peripherals, PCIe, SoC blocks | Legacy PC hardware, some ISA-era chips |
Inspecting Ports on a Running System
Linux exposes a live map of claimed port ranges through the proc filesystem. This is a quick, practical way to see which drivers currently own which ports on an x86 machine.
$ sudo cat /proc/ioports
0000-0cf7 : PCI Bus 0000:00
0060-0060 : keyboard
0070-0071 : rtc_cmos
03f8-03ff : serial
0cf8-0cff : PCI conf1
Each line lists a claimed range and the driver name that owns it, which is exactly the reservation bookkeeping described above.
Real-World Use Cases
- Legacy PC peripherals — real-time clock, legacy serial/parallel ports, PS/2-style controllers
- PCI configuration access on some older x86 platforms
- Bootloader / firmware level chip access before richer MMIO resources are set up
Outside these cases, modern embedded Linux work — BLE controllers, sensors, display panels — is almost entirely MMIO based, which is why this course covers MMIO first and PMIO as supplementary, historically important knowledge.
Common Mistakes and Troubleshooting
| Mistake | Why It Hurts | Fix |
|---|---|---|
| Skipping the region reservation step | Two drivers can silently clash on the same port | Always reserve before first access |
| Assuming PMIO exists on ARM | No physical port space on most SoCs | Use MMIO on non-x86 targets |
| Forgetting to release ports on unload | Leaves a stale, unusable reservation | Prefer managed/lifetime-bound reservation |
Best Practices for Port Mapped I/O in Linux
- Always check the peripheral’s datasheet — never assume a port address
- Prefer managed resource APIs so cleanup happens automatically
- Keep port access code isolated in small helper functions for readability
- Treat PMIO as legacy-support code on new x86 designs, not the default approach
Security and Performance Considerations
From a security standpoint, unrestricted port I/O access is a privileged operation gated by the kernel; user-space processes cannot touch arbitrary ports without explicit, deliberately narrow permission grants, which keeps a compromised application from reprogramming arbitrary hardware. From a performance standpoint, each port instruction still requires the CPU to pause and wait on the peripheral bus, so PMIO — like MMIO — is unsuitable for high-throughput data transfer; that job belongs to DMA, a topic covered in a later lecture of this free Linux device drivers course.
Summary / Key Takeaways
- Port mapped I/O uses a separate address space, reached with dedicated CPU instructions
- It is mainly an x86 feature; most embedded ARM/RISC-V systems only have MMIO
- The kernel requires drivers to reserve port ranges before touching hardware
- PMIO is best understood today as legacy-support knowledge, not the default I/O method
Conclusion
Understanding port mapped I/O in the Linux kernel rounds out your picture of how drivers talk to hardware, even though most new embedded designs lean on MMIO. Knowing when PMIO shows up — legacy x86 peripherals, boot-time chip access — means you will recognize it instantly in real driver source trees instead of being confused by it. In the next lecture of this free Linux kernel programming course, we move from concepts to code: reserving a port region and performing an actual register read/write from a kernel module.
Frequently Asked Questions
Q1. Is port mapped I/O still relevant in 2026?
It remains relevant for legacy x86 peripherals and bring-up code, but new peripheral designs almost universally use MMIO.
Q2. Does ARM support port mapped I/O?
Not in hardware. ARM and most modern embedded processors only expose memory-mapped registers.
Q3. What is the difference between a “port” here and a network port?
They are unrelated concepts that happen to share a name; a hardware port is an address slot for a register, not a network endpoint.
Q4. How wide can a port register be?
Typically 8, 16, or 32 bits, depending on the peripheral.
Q5. Can user-space applications access ports directly?
Only with explicit, narrowly scoped kernel permission; it is not available by default for security reasons.
Q6. Where can I see which ports are in use on my machine?
The /proc/ioports pseudo-file lists every currently reserved port range and its owning driver.
Q7. Is this lecture part of a complete course?
Yes — it is one lecture in EmbeddedPathashala’s free Linux kernel programming and device drivers course.
More lectures on device drivers, MMIO, and kernel internals are coming up next.
Browse the Full Course
2 Comments