I am trying to understand how IO devices are mapped into the 'regular' memory address space on modern x86 machines running Linux.
Some details which I am trying to make sense of are:
cat /proc/iomemprints out a list of io memory mapped regions (printing the physical addresses) which are non-contiguousThese regions can be requested by kernel modules dynamically at runtime, and allocated via the function request_mem_region which is defined in <linux/ioport.h>
x86 machines use
movfor both memory-access and IO (that is mapped into memory)
So now, supposing kernel module code is running, we will likely encounter an instruction like mov [value] [virtual address] where the virtual address could be referring to either an mmio region or 'normal' data values that exist in memory. The process to separate mmio traffic from 'normal' memory ought to have 2 key steps:
- determine if the address is mmio. The page table has a flag for whether it is memory-mapped, so I assume the mmu determines this while doing page table translation.
- Determine the 'IO address' of the newly produced physical address (that the mmu gave as output from the page table translation) and pass this to whatever chip interfaces with the IO (Northbridge, root complex, etc)
Question 1: is my understanding of step-1 above correct?
Question 2: How is step-2 carried out? (by what entity and how is the map stored?)
The ranges that need to be checked are listed in /proc/iomem, and the data which it draws from I guess is a map that looks like:
map[mmio_address] = io_address_object
Keeping in mind that all of this is happening within the context of a single mov instruction from the perspective of the cpu, I can't see how this translation could happen via anything other than hardware external to the cpu.