mmio trace for fun and no-profit

I’m not sure what got me interested in assembly language as a nerdy high-schooler. It could have been the growing interest in computer graphics, at a time when you had to use assembly to get decent performance out of the machine. I remember learning tricks from much smarter people than myself, such as how to set a 256×256 pixel video mode so that you could address any pixel without ever needing to multiply. Not that you would use a multiply anyway since you can always use shifts and adds.

I suspect, however, that part of what spurred me on was an interest in reverse-engineering, specifically to crack games. You would start BattleChess, say, and it would ask you for some move from some historical chess game in a big book before letting you play. This was to ensure that if you copied the game, that you at least also photocopied your friend’s manual. [For the record, while I cracked a few games, I never released any such cracks. More from being l4m3 than any teenage sense of ethics. I did, however, release instructions for patching the video game “Home Alone” so that you could never die. I may have been the only person on the planet to play that one all the way through.]

In order to crack a game, first you would load up SoftICE, a killer software debugger that would let you debug almost anything. It would let you set breakpoints on interrupts so step one was to put a breakpoint on ‘int 10h’. Interrupt 0x10 was a call into the video BIOS for setting up the video card. Back in the DOS days, it was the first thing every graphical program (and therefore game) did, because you started out in text mode and had to go into VGA mode. The BIOS would know how to load all the registers for that particular card; you just had to say “put me in 320×200 mode, now, thanks!” and that was via the assembly command “int 10h”.

After SoftICE caught the interrupt, the screen would switch back from VGA mode into a text mode listing of assembly instructions and raw machine code hex values. I don’t think I knew what all these meant at first, but I understood ‘call’ and ‘jmp’, and everything else I quickly learned from a text file describing the x86 instruction set architecture. So cracking then became a matter of just single stepping through all of these instructions, and waiting for BattleChess to get to the part where it asks you the question. Then, you start paying attention, looking for the assembly equivalent of “Is the answer right? If so, goto game! Else goto nasty message!” Some of these were harder than others, but generally it looked something like:

mov ax, ds:[43ac]
mov cx, ds:[3401]
cmp ax, cx
jnz 0027

You could step through it, type the wrong answer, then watch it jump to instruction number 27 which then printed out the nasty message. Then you could try again, and this time modify the code while you’re in the debugger. For example, change the jnz 0027 to a few no-ops (instructions that do nothing). Type in the wrong answer, and now it keeps on going to the game. Bingo, the game is cracked! SoftICE was such a nice debugger that I even used it for C debugging until I finally moved off of that whole DOS thing.

Anyway, I’ll never be a Jon Johansen, but that interest in reversing stuff has stuck with me. I think that figuring out how the Karma worked, which involved many hours of pouring over hex dumps trying to come up with the pattern, was much more fun than coding the driver. Particularly when the “ah-hah!” moments would strike. Incidentally, coding the driver was/is still fun, probably more so than actually using the device.

This little walk down memory lane was inspired by the recent entrance of mmio-trace into my zone of consciousness. I’m not sure who used this first, but the Nouveau project has been using the utility for some time to reverse-engineer the NVidia video cards so that Linux can gain decent open source drivers. My laptop luckily has an Intel based video card, but it does have an Atheros wireless chip which also has a binary blob. There’s currently an effort to produce a reverse-engineered driver for it as well. In my case, the new driver almost worked, but I needed to come up with some other details, so I tried out mmio-trace.

PCI cards are configured by register writes, but the driver generally has to program it to get it going. The BIOS can’t do that work for us any more. So the OS maps a region of memory that the driver can just write into, which then all gets converted magically into writes into the PCI device’s register file. Thus, MMIO=”Memory Mapped IO”, and mmio-trace does what it sounds like. Using it is rather painless, you first ‘hook’ a module you want to capture, modprobe mmiotrace, then load the hooked module. All writes to PCI config space get captured into a debugfs file that you then run through a user-space filter. The end result is a nice report of all of the reads and writes to the registers of the PCI device. Very neat!

Oh, the driver maintainer already knew about the issue and confirmed my proposed change so hopefully by 2.6.26 ath5k will support my Macbook.