1

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.

1
  • 2
    No, it is not possible to map the I/O to a virtual address space. Read the implementation of the ioread() and iowrite() 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. Commented Dec 20, 2020 at 15:30

1 Answer 1

2

Note: this answer is x86 specific.

I/O ports are accessed through the IN and OUT instructions.

It should be possible to mmap a bounce buffer which a driver manages, and write out to serial using those instructions. You will probably need to write an interface for synchronization (e.g. a flush ioctl).

Unlike what you might be able to do with and MMIO based device, you won’t be able to sketchily directly map the memory that touches the device into user space, since that memory does not exist.

You could try to muck with I/O port permissions so that userspace has access to the I/O port itself. The ioperm syscall (and maybe others) manage this.

Sign up to request clarification or add additional context in comments.

1 Comment

@rutherford got it, io ports do not have an underlying struct page, so cannot be mmaped, If I try to calculate the PFN based on the IO address, and use it, it would corrupt some other valid memory. I was wondering if ioport_map does anything in terms of creating a page or mapping in the MMIO address space, since after ioport_map, I am able to use ioread() and iowrite() MMIO instructions perfectly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.