I am using NetBSD 5.1 for x86 systems. While studying some driver related code, I see that we use splraise and spllower to block or allow interrupts. I searched some of the mechanisms on internet to understand how these mechanisms work in reality. Did not get any real info on that.
When I disassembled I got the mechanism but still do not understand how all these assembly instruction yield me the result. I know x86 instruction individually, but not how the whole stuff works in its entirety.
Need your help in understanding its principles for x86 system. I understand that we need to disable Interrupt Enable (IE) bit, but this assembly seems to be doing more than just this work. Need help.
(gdb) x/50i splraise
0xc0100d40: mov 0x4(%esp),%edx
0xc0100d44: mov %fs:0x214,%eax
0xc0100d4a: cmp %edx,%eax
0xc0100d4c: ja 0xc0100d55
0xc0100d4e: mov %edx,%fs:0x214
0xc0100d55: ret
0xc0100d56: lea 0x0(%esi),%esi
0xc0100d59: lea 0x0(%edi,%eiz,1),%edi
(gdb) p spllower
$38 = {<text variable, no debug info>} 0xc0100d60
0xc0100d60: mov 0x4(%esp),%ecx
0xc0100d64: mov %fs:0x214,%edx
0xc0100d6b: cmp %edx,%ecx
0xc0100d6d: push %ebx
0xc0100d6e: jae,pn 0xc0100d8f
0xc0100d71: mov %fs:0x210,%eax
0xc0100d77: test %eax,%fs:0x244(,%ecx,4)
0xc0100d7f: mov %eax,%ebx
0xc0100d81: jne,pn 0xc0100d91
0xc0100d84: cmpxchg8b %fs:0x210
0xc0100d8c: jne,pn 0xc0100d71
0xc0100d8f: pop %ebx
0xc0100d90: ret
0xc0100d91: pop %ebx
0xc0100d92: jmp 0xc0100df0
0xc0100d97: mov %esi,%esi
0xc0100d99: lea 0x0(%edi,%eiz,1),%edi
0xc0100da0: mov 0x4(%esp),%ecx
0xc0100da4: mov %fs:0x214,%edx
0xc0100dab: cmp %edx,%ecx
0xc0100dad: push %ebx
0xc0100dae: jae,pn 0xc0100dcf
0xc0100db1: mov %fs:0x210,%eax
0xc0100db7: test %eax,%fs:0x244(,%ecx,4)
0xc0100dbf: mov %eax,%ebx
0xc0100dc1: jne,pn 0xc0100dd1
0xc0100dc4: cmpxchg8b %fs:0x210
0xc0100dcc: jne,pn 0xc0100db1
0xc0100dcf: pop %ebx
0xc0100dd0: ret
0xc0100dd1: pop %ebx
0xc0100dd2: jmp 0xc0100df0
0xc0100dd7: mov %esi,%esi
0xc0100dd9: lea 0x0(%edi,%eiz,1),%edi
0xc0100de0: nop
0xc0100de1: jmp 0xc0100df0
The code seems to be using a helper function cx8_spllower starting at address 0xc0100da0.
Clearing
[E|R]FLAGS.IEwith e.g.CLIdisables all (maskable) interrupts on a CPU. For a number of reasons it may be undesirable (e.g. you want to allow some or, perhaps, you don't want overheads of virtualizingCLIin a VM).Another way of achieving the goal is to tell to the interrupt controller (either the old 8259 PIC or the Pentium APIC/IOAPIC) that you don't want to service interrupts whose priority is below a certain level. For that you need to communicate with the controller, which itself may incur additional overhead (talking to both real and virtual hardware is slow).
A variant of the latter is to keep the current interrupt level/priority in a variable and let interrupts come in, but only really service those whose level/priority isn't lower than the current one in that variable. Those unserviced will be marked as pending and will get fully serviced when the current level/priority drops sufficiently low. This is why
splraise()is much simpler thanspllower().This level/priority variable appears to be named differently in different places and versions of the code:
CPL(not to be confused with CPU's current privilege level),SPL(?),ILEVEL.This is my current, limited understanding of the implementation. There are more details to it.
Here are some clues that I've found and used for the answer: