I was checking if the io-port address of serial port (uart) 0x2f8 could be mmapped to user space. I wanted to do this myself in the driver instead of using any libraries to know which api's are needed to achieve this. However, I am not sure if it can actually be done, given that, this is x86 and port IO specific. Irrespective, I have written an mmap function and caller in User space.
Setting up the mapping is as below:
res = request_region(base_addr, 8 ,"custom_serial_device");
void __iomem * mem_base_addr = ioport_map(base_addr, 8);
iowrite8(0x01, (u8 *)my_dev->mem_base_addr + 3); /*Few more configs and works fine */
/* After this I am able to make the uart function in software loopback mode */
Now I decided to try mmap on this address:
static int my_dev_mmap(struct file *filep, struct vm_area_struct *vma) {
SERIAL_DEV *my_dev = (SERIAL_DEV*)filep->private_data;
unsigned long pfn;
size_t sz = vma->vm_end - vma->vm_start;
unsigned long phys_addr;
if (vma->vm_end - vma->vm_start != PAGE_SIZE) {
return -EINVAL;
}
if (PAGE_SIZE > (1 << 16))
return -ENOSYS;
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_flags |= VM_IO;
/*ioport_map gives virtual address so I am using below to get physical addr */
phys_addr = virt_to_phys(my_dev->mem_base_addr);
pfn = phys_addr >> PAGE_SHIFT;
printk("GNA: mmap called: vm-start: 0x%lx vm-end: 0x%lx mem_base: 0x%lx phys: 0x%lx pfn: %lu\n",
vma->vm_start, vma->vm_end, (unsigned long)my_dev->mem_base_addr, phys_addr, pfn);
if (remap_pfn_range(vma, vma->vm_start, pfn, sz, vma->vm_page_prot)) {
printk("GNA: mmap failed\n");
return -EAGAIN;
}
return 0;
}
The print I get is below:
Dec 19 11:05:00 realtek-dpdk kernel: [ 4624.894086] GNA: mmap called:
vm-start: 0x7fa150f4d000 --> Kernel allocates this range which is in user space
vm-end: 0x7fa150f4e000
mem_base: 0x102f8
phys: 0x6587800102f8 --> Virt_to_phys gave this which seems wrong
pfn: 27254063120
When I print the mmap'd memory I am expecting 0x5A at offset 0x7 (scratch pad) from 0x2f8 (uart's base). However I do not see the expected output.
The other method I tried is given that ioport_map gave a address 0x102f8, which doesn't seem like a virtual address but more of a physical address (BASE + 0x2f8) So, I directly used this address to get the pfn. But still same result.
My userspace program is as below:
address = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, configfd, 0);
After this I am trying to find the value written in the scratch pad memory at offset 0x7.
for (int i = 0 ; i < 10; i++) {
printf("Value: 0x%x 0x%x\n", address[i], address[0x2f8 + i]);
}
So, is it possible to mmap() an ioport. If yes, can you please tell the correct steps for this.
PS: although this is x86 I have added embedded-linux tag to get some help from platform side as well incase anyone knows the difference.
ioread()andiowrite()to get the trick (yea, you’re on the half way by noticing that address is just 0x10000 + I/O port, obviously why — I/O space is 16-bit wide, this address is simply first non overlapping with port offsets). And yes, this is x86 only, on other architectures there is no concept of I/O port.