0
votes

I'm trying to access I/O ports from C, on Linux (Ubuntu), via IN and OUT instructions in inlined assembly code. A seg fault is generated as soon as an IN or OUT instruction is executed.

For example, this simple piece of code generates a seg fault:

#include <stdint.h>

int main() {

    uint8_t readvalue = 0;
    uint16_t port = 0xB3;

    asm volatile("in    %%dx, %%al\n\t"
         : "=a" (readvalue)
         : "d" (port)
    );

    return(0);

}

I compile with: gcc -O2 -g

From GDB, I see that this program was compiled into the following simple assembly code sequence:

mov    $0xb3,%edx
in     (%dx),%al
xor    %eax,%eax
retq

And in GDB, I see that as soon as the IN instruction is executed, a seg fault occurs.

The full GDB session:

(gdb) list 
1   #include <stdint.h>
2   
3   int main() {
4       
5       uint8_t readvalue = 0;
6       uint16_t port = 0xB3;
7            
8       asm volatile("in    %%dx, %%al\n\t"
9            : "=a" (readvalue)
10           : "d" (port)
(gdb) list
11      );
12
13      return(0);
14
15  } 
16   
(gdb) break 7
Breakpoint 1 at 0x4003e0: file tcgsmi_inonly_pure.c, line 7.
(gdb) r
Starting program: /home/emerald/tcgsmi_inonly_pure

Breakpoint 1, main () at tcgsmi_inonly_pure.c:8
8       asm volatile("in    %%dx, %%al\n\t"
(gdb) disass
Dump of assembler code for function main:
=> 0x00000000004003e0 <+0>: mov    $0xb3,%edx
   0x00000000004003e5 <+5>: in     (%dx),%al
   0x00000000004003e6 <+6>: xor    %eax,%eax
   0x00000000004003e8 <+8>: retq
End of assembler dump.
(gdb) display/i $rip
1: x/i $rip
=> 0x4003e0 <main>: mov    $0xb3,%edx
(gdb) ni
0x00000000004003e5  8       asm volatile("in    %%dx, %%al\n\t"
1: x/i $rip
=> 0x4003e5 <main+5>:   in     (%dx),%al
(gdb) ni

Program received signal SIGSEGV, Segmentation fault.
0x00000000004003e5 in main () at tcgsmi_inonly_pure.c:8
8       asm volatile("in    %%dx, %%al\n\t"
1: x/i $rip
=> 0x4003e5 <main+5>:   in     (%dx),%al
1
Are you running this in user mode or kernel mode? User programs can't use IN and OUT like that.Greg Hewgill
You may find the following documentation informative: The Userspace I/O HOWTOGreg Hewgill
@GregHewgill: more specifically, to allow in / out from userspace on x86, call the ioperm function (as root) to allow permissions on specific ports. Or iopl to set I/O privilege level for all ports.Peter Cordes
Also note that there's a very good reason that I/O ports are protected. If you start experimenting with them, be prepared for your system to crash.prl

1 Answers

1
votes

Answer is in the comments on the OP.

IO ports cannot simply be accessed in user mode.

  1. The program needs to be run as root (e.g. with sudo).
  2. Before accessing the ports, they need to be unlocked with ioperm().

The prototype for ioperm() is:

int ioperm(unsigned long from, unsigned long num, int turn_on);

from = start ID of range of ports to which you want access
num = length of range
turn_on = nonzero to enable access, zero to disable access

So, in the example above, the call would be:

ioperm(0xB3,1,1);