2

I'm trying to write byte 0xff to the parallel port at 0x378. It compiles and links without issue, but segfaults at the OUTSB instruction.

section .text
        global _start

_err_exit:
        mov     eax,    1
        mov     ebx,    1
        int     80h

_start:
        mov     eax,    101     ; ioperm
        mov     ebx,    0x378   ; Parallel port addr
        mov     ecx,    2       ; number of bytes to 'unlock'
        mov     edx,    1       ; enable
        int     80h

        mov     esi,    0xff
        mov     dx,     0x378
        outsb

        mov     eax,    1       ; exit
        mov     ebx,    0
        int     80h

If I step through it with GDB and check the registers just before the OUTSB instruction, it doesn't look like there is anything in the DX register? or dx == edx in 32bit?

(gdb) info registers 
eax            0x0  0
ecx            0x2  2
edx            0x378    888
ebx            0x378    888
esp            0xffffd810   0xffffd810
ebp            0x0  0x0
esi            0xff 255
edi            0x0  0
eip            0x8048090    0x8048090 <_start+36>
eflags         0x246    [ PF ZF IF ]
cs             0x23 35
ss             0x2b 43
ds             0x2b 43
es             0x2b 43
fs             0x0  0
gs             0x0  0

What am I doing wrong here?

(info on the OUTS instructions: http://siyobik.info/main/reference/instruction/OUTS%2FOUTSB%2FOUTSW%2FOUTSD)


EDIT:

The C version of the program works:

int main(int argc, char *argv[])
{
  int addr = 0x378;
  int result = ioperm(addr,5,1);

  outb(0xff, addr);

}
tMC
  • 18,105
  • 14
  • 62
  • 98

1 Answers1

4

There is a number of issues with that code. Firstly, you seem to forget that OUTSB is a privileged instruction, i.e. it can be executed only if the calling process has ring 0 access, i.e. it's a part of the kernel code. As far as I'm aware, the only code in Linux that has access to privileged instructions is the kernel itself, and the modules that it loads. All the other processes will give you a Segmentation fault (which is actually a General Protection Fault signalled by the CPU) when you try to execute a privileged instruction from a nonprivileged segment of code. I don't know how calling the ioperm syscall influences that, though.

Secondly, OUTSB writes a byte from a memory location specified by ESI to the I/O port in DX. In this case, you're telling the processor to write data to the port from location 0xff, to which the process surely doesn't have access. You can simplify that by simply changing the code to use the OUT instruction, since OUTSB is rather meant to be used with the REP prefix. Try this :

mov al, 0xff
out 0x378, al

This outputs the byte in al to the I/O port specified by the immediate operand, in this case 0x378.

Let me know how that turned out.

Daniel Kamil Kozar
  • 18,476
  • 5
  • 50
  • 64
  • On another note : I just revised some of the Intel manuals and it's apparently possible to grant permissions to individual I/O ports to individual tasks. This requires the use of the TSS (Task State Segment) structure in the OS, and I'm not sure if Linux uses it for each individual process. – Daniel Kamil Kozar Feb 24 '12 at 00:44
  • `OUT` is as privileged as `OUTS*`, replacing one with the other won't fix the main problem. – Alexey Frunze Feb 24 '12 at 00:48
  • 1
    I know that. But `OUTSB` in this case is used improperly because it causes a read from a memory region that's most certainly not in the process' address space. – Daniel Kamil Kozar Feb 24 '12 at 06:22
  • Changing `OUTSB` to `OUT 0x378, al` still SegFaults. It also complains when assembling: `porttest.asm:21: warning: unsigned byte value exceeds bounds` – tMC Feb 24 '12 at 14:26
  • 1
    @Daniel I edited my post- The C version of the program works (when run as the root user in userland). the `outb()` function is defined in `sys/io.h`. `static __inline void outb (unsigned char __value, unsigned short int __port) { __asm__ __volatile__ ("outb %b0,%w1": :"a" (__value), "Nd" (__port)); }` – tMC Feb 24 '12 at 14:34
  • 1
    Thanks for the update! Apparently, `ioperm` modifies the I/O permission port map of the task somehow and allows it to execute privileged I/O instructions. – Daniel Kamil Kozar Feb 24 '12 at 15:04
  • 1
    @tMC: use `out dx,al`. From your own reference, on OUT: http://siyobik.info/main/reference/instruction/OUT : _the destination operand can be a byte-immediate or the DX register. Using a byte immediate allows I/O port addresses 0 to 255 to be accessed; using the DX register as a source operand allows I/O ports from 0 to 65,535 to be accessed._ – ninjalj Feb 26 '12 at 23:57
  • @tMC: Daniel's answer deserves the points for its second paragraph. – ninjalj Feb 27 '12 at 19:33