IOport : Access Input/Output ports in Mocka executables
As a true hacking geek, I needed to access the I/O ports of the PC. Under DOS that was no big deal, but under Linux it's different. I know I/O ports should be shielded most of the time by the operating system, but there are times that you simply need to flip some switches.
Switches on the LPT port (aka Geek Port) to be precise. To get all those projects working from our DOS era's.
So I decided it was time to bend some rules and extend Mocka with a small library that enables direct port access from within Mocka executables.
This library goes down to the bare bones of the computing platform. I work on a PC running Slackware Linux. So that's the hardware I will support and since I know my way with assembly language, I made the actual interface code in AT&T style 80386 code.
The library consists of two parts:
In order to test what I just made, I needed to make a small program. This is what is included as 'prf'. It checks the workings of the module.
IOport.md, the FOREIGN MODULE.
FOREIGN MODULE IOport; (* This module supports IO port access under Mocka / Linux CopyLeft Jan 2004, Jan Verhoeven. This module is published under de rules of the GNU GPL. To change the working of this material do as follows: $ jed IOport.asm $ as IOport.asm -o IOport.o and you get an improved version. Improvements and normal use are entirely on your own risk. Please feel free to improve this software, if needed. You may even port this source to a lesser language like C. This software comes without any warranty of any kind. *) (* 01 : Get a working MODULE, no frills. Jan 02, 2004 *) PROCEDURE InPort (port : CARDINAL) : CARDINAL; (* Read a byte from I/O address 'port' and return it to the caller *) PROCEDURE OutPort (port, value : CARDINAL); (* Write a byte 'value' to I/O address 'port' *) PROCEDURE IOperm (from, to : CARDINAL; value : BOOLEAN) : BOOLEAN; (* Ask unix permission to access the specified I/O addresses: from = first port to access, to = last port to access, value = TRUE or FALSE. The values of the parameters 'from' and 'to' are inclusive. So the snippet: IF IOperm (0x378, 0x37A, TRUE) = FALSE THEN WriteString ('Could not get access to I/O ports.'); WriteLn END; asks the operating system to access ports 0x378, 0x379 and 0x37A. If the caller has enough privileges, the access is granted and the IF statement is not executed. *) END IOport.This was the easy part. Now for something completely different.
IOport.asm, the assembly source.
What follows is the assembly language sourcecode for the interface routines. These are all rather
straighforward for all people who have ever tried to do something similar under DOS.
The main difference is the (at first) odd syntax of the AS assembler. But the syntax is not THAT odd. It's just a lot like Modula-2.... All registernames must be prefixed by a percent sign (%). All data values must be prefixed by a dollar sign ($). But that's all done to protect the programmer against his or her own speed.
The main problem is the reversed operand order. We all got used to the Intel syntax which is completely erratic. And now, if we go to the AT&T syntax (which is logical), we have problems since we got used to a bad predecessor system....
See for yourself what I mean. If you don't like this syntax, use NASM. Everything following a '#' symbol is a comment (until the end of the line).
# IMPLEMENTATION MODULE PortIO; .globl InPort .globl OutPort .globl IOperm .text .align 4 # PROCEDURE InPort (port : CARDINAL) : CARDINAL; InPort: pushl %ebp # stack up EBP movl %esp, %ebp # EBP := ESP movl 8(%ebp), %edx # EDX := port movl $0000, %eax # EAX := 0 inb %dx, %al # EAX := port value movl %ebp, %esp # ESP := EBP popl %ebp # restore old value of EBP ret # PROCEDURE OutPort (port, value : CARDINAL); OutPort: pushl %ebp movl %esp, %ebp # set up EBP movl 8(%ebp), %edx # EDX := port movl 12(%ebp), %eax # EAX := value outb %al, %dx # write to I/O port movl %ebp, %esp popl %ebp ret # PROCEDURE IOperm (from, to : CARDINAL; value : BOOLEAN) : BOOLEAN; IOperm: pushl %ebp movl %esp, %ebp # save EBP and set up new EBP movl 8(%ebp), %ebx # EBX := first port movl 12(%ebp), %ecx # ECX := last port subl %ebx, %ecx # ECX := ECX - EBX + 1 incl %ecx # ECX := nr of ports jecxz IOpermError movl 16(%ebp), %edx # EDX := flag movl $101, %eax # EAX := code for IOperm call int $0x80 # perform SYSTEM call movl $0001, %eax # EAX := 0001 sbbl $0000, %eax # IF Error THEN EAX := 0000 jmp IOpermExit IOpermError: movl $0000, %eax IOpermExit: movl %ebp, %esp popl %ebp retYou assemble the source with the command:
bash-2.05$ as IOport.asm -o IOport.oWith the 0608m compiler: copy the object file to the m2bin subdirectory. That's it. Commence now as usual:
At this moment I feel tempted to make more libraries in pure assembly language. With a list of some Linux Systemcalls (using INT 0x80), the full InOut library could be rewritten to snugly fit the PC hardware. And to come close to FST Modula-2.
prf.mi, the test software.
What follows is a typical testing program. It does nothing sensible and it is littered with WriteString
In first instance this program was called 'test.mi' and the executable was called 'test'. This caused a lot of confusion since there is a Linux program called 'test' as well and it is in a directory which is early in the searchpath. So every time I wanted to try my 'test' program, the other one was invoked.
So if you make a 'test' program under Linux do NOT call it 'test'!
MODULE prf; FROM IOport IMPORT InPort, OutPort, IOperm; FROM InOut IMPORT WriteString, WriteLn, WriteCard, WriteBf; VAR ioPerm : BOOLEAN; ioVal : CARDINAL; BEGIN ioPerm := FALSE; WriteString ('Start....'); WriteLn; WriteString ('ioPerm = '); IF ioPerm = TRUE THEN WriteString ('TRUE.') ELSE WriteString ('FALSE.') END; WriteLn; WriteBf; ioPerm := IOperm (378H, 37AH, TRUE); IF ioPerm = FALSE THEN WriteString ('No permission to access ports 378 - 37A.'); WriteLn; HALT ELSE WriteString ('Permission received to access ports 378 - 37A.'); WriteLn END; WriteString ('ioPerm = '); IF ioPerm = TRUE THEN WriteString ('TRUE.') ELSE WriteString ('FALSE.') END; WriteLn; WriteString ('We have executed the IOperm function and are out of it again.'); WriteLn; WriteString ('Coming up now: OutPort and InPort and the WriteCard.'); WriteLn; WriteBf; OutPort (378H, 1); ioVal := InPort (378H); WriteCard (ioVal, 4); WriteLn; WriteString ('The WriteCard has executed. Time to redeem IOperm.'); WriteLn; WriteString ('ioPerm = '); IF ioPerm = TRUE THEN WriteString ('TRUE.') ELSE WriteString ('FALSE.') END; WriteLn; WriteBf; IF ioPerm = TRUE THEN WriteString ("ioPerm = TRUE and we're gonna give it back."); WriteLn; WriteBf; ioPerm := IOperm (378H, 37AH, FALSE); IF ioPerm = FALSE THEN WriteString ('Could not return I/O privileges.'); WriteLn; WriteString ('Aborting.'); WriteBf; HALT ELSE WriteString ('I/O privileges successfully returned.'); WriteLn END; WriteBf ELSE WriteString ('If this message is printed, something fishy is going on.'); WriteLn END; WriteBf END prf.OK, lads, now download and use it.
Oh, I almost forgot: you need to give your program enough privileges, otherwise it will be denied to call the IOperm system call. After compiling your source, you must become 'root' and then enter the following magic spells:
bash-2.05# chown root.root prf bash-2.05# chmod 4755 prfA little swearing is allowed but a magic wand is an accessory and it will cost you extra. The wand is not covered by the GNU GPL, contrary to the sources you saw until now on this page.
If you forget to chown and chmod the executable you end up with this:
jan@beryllium:~/modula/lib$ ./prf Start.... ioPerm = FALSE. Permission received to access ports 378 - 37A. ioPerm = TRUE. We have executed the IOperm function and are out of it again. Coming up now: OutPort and InPort and the WriteCard. Segmentation faultForget either of the two and you get SegFaults by the dozen...
Page created 8 August 2004,