I wrote this class to use a C++ function as hardware interrupt handler. The idea is to have a single entry point where I can switch stacks, and call a chain of std::function
objects to handle the interrupt.
It works... but I think it's a pretty ugly hack. Is there anything I could improve here?
class __attribute__((packed)) interrupt_wrapper
{
using function_ptr = void(*)(unsigned);
unsigned int_vector; // [eax-0x1C]
selector ds; // [eax-0x18]
selector es; // [eax-0x16]
selector fs; // [eax-0x14]
selector gs; // [eax-0x12]
function_ptr entry_point; // [eax-0x10]
std::array<byte, 0x40> code; // [eax-0x0C]
public:
interrupt_wrapper(unsigned vec, function_ptr f) : int_vector(vec), entry_point(f)
{
byte* start;
std::size_t size;
asm volatile (
".intel_syntax noprefix;"
"jmp interrupt_wrapper_end%=;"
// --- \/\/\/\/\/\/ --- //
"interrupt_wrapper_begin%=:;" // On entry, the only known register is CS.
"push ds; push es; push fs; push gs; pusha;" // 7 bytes
"call get_eip%=;" // call near/relative (E8) // 5 bytes
"get_eip%=: pop eax;" // Pop EIP into EAX and use it to find our variables
"mov ds, cs:[eax-0x18];" // Restore segment registers
"mov es, cs:[eax-0x16];"
"mov fs, cs:[eax-0x14];"
"mov gs, cs:[eax-0x12];"
"push cs:[eax-0x1C];" // Pass our interrupt number along
"call cs:[eax-0x10];" // Call the entry point
"add esp, 4;"
"popa; pop gs; pop fs; pop es; pop ds;"
"sti;" // IRET is not guaranteed to set the interrupt flag.
"iret;"
"interrupt_wrapper_end%=:;"
// --- /\/\/\/\/\/\ --- //
"mov %0, offset interrupt_wrapper_begin%=;"
"mov %1, offset interrupt_wrapper_end%=;"
"sub %1, %0;" // size = end - begin
".att_syntax prefix"
: "=r" (start)
, "=r" (size));
assert(size <= code.size());
auto* ptr = memory_descriptor(get_cs(), start).get_ptr<byte>(); // Get near pointer to cs:[start]
std::copy_n(ptr, size, code.data());
asm volatile (
".intel_syntax noprefix;"
"mov ax, ds;"
"mov bx, es;"
"mov cx, fs;"
"mov dx, gs;"
".att_syntax prefix"
: "=a" (ds)
, "=b" (es)
, "=c" (fs)
, "=d" (gs));
}
auto get_ptr(selector cs) { return far_ptr32 { cs, reinterpret_cast<std::size_t>(code.data()) }; }
};