Today I changed the interrupt routine system (finally!) I added the architecture-dependent interrupt wrapper to all the general interrupts, now making the entire interrupt routine fully independent of the architecture.
The kernel now has 256 pre-declared interrupt handlers that work as the pre-declared entry point for all the registered interrupt numbers. If we make the pre-declared interrupt wrappers to call architecture-specific wrapping function, we can finally "generalize" the interrupt routine across the architectures.
/* In kernel/src/interrupt/predeclared_interrupt_handlers.cpp */
interrupt_handler_t interrupt::handler::get_general_int_wrapper(int index) {
INTERRUPT_GENERAL_INT_WRAPPER_ARRAY
if(index >= INTERRUPT_GENERAL_INT_WRAPPER_MAXCOUNT) return 0x00;
return general_int_wrapper_array[index];
}
INTERRUPT_GENERAL_INT_WRAPPER_HANDLER_FUNCTION(0)
INTERRUPT_GENERAL_INT_WRAPPER_HANDLER_FUNCTION(1)
INTERRUPT_GENERAL_INT_WRAPPER_HANDLER_FUNCTION(2)
INTERRUPT_GENERAL_INT_WRAPPER_HANDLER_FUNCTION(3)
INTERRUPT_GENERAL_INT_WRAPPER_HANDLER_FUNCTION(4)
...
INTERRUPT_GENERAL_INT_WRAPPER_HANDLER_FUNCTION(255)
You can also see that I placed the declaration of interrupt handler's wrapper function on the architecture-dependent side of project:
/* In arch/x86_64/include/x86_64/interrupt_wrapper.hpp */
#ifndef _INTERRUPT_WRAPPER_HPP_
#define _INTERRUPT_WRAPPER_HPP_
#include <kernel/types.hpp>
#include <kernel/interrupt/interrupt.hpp>
#include <kernel/interrupt/predeclared_interrupt_handlers.hpp>
#define INTERRUPT_START \
/* architecture-specific implementation of the start of interrupt */
#define INTERRUPT_END \
/* architecture-specific implementation of the end of interrupt */
#define INTERRUPT_GENERAL_INT_WRAPPER_HANDLER_FUNCTION(handler_num) \
/* architecture-specific implementation of interrupt wrapper function */
/* purpose: to call the actual interrupt handler stored in the interrupt manager */
#define INTERRUPT_HARDWARE_SPECIFIED_WRAPPER_HANDLER_FUNCTION(handler_num) \
/* architecture-specific implementation of interrupt wrapper function */
/* purpose: to call the actual interrupt handler stored in the interrupt manager */
#endif
This is the very general form of it, but practical implementation quickly becomes nasty.
The kernel requires only the implementation of INTERRUPT_GENERAL_INT_WRAPPER_HANDLER_FUNCTION and INTERRUPT_HARDWARE_SPECIFIED_WRAPPER_HANDLER_FUNCTION. (INTERRUPT_START and INTERRUPT_END macros are just there to make the codes more concise and simplified.)
(Note that those two functions must be naked/interrupt-specific functions.)
Objective (Sequentially) :
It's very straightforward. The only problem for me was that I had to tinker manually with stack addresses, which I'm not that bright at. Either way, I somehow managed to implement this entire process! It only took like 200 lines.. haha. Because it was a naked function, the C++ variables that are declared were all naked registers not the stacks, so I had bit of problem dealing with local variables without mangling the original register values...
#define INTERRUPT_GENERAL_INT_WRAPPER_HANDLER_FUNCTION(handler_num) \
__attribute__ ((naked)) void interrupt::handler::general_wrapper##handler_num(void) {\
INTERRUPT_START \
interrupt_handler_t handler;\
if((handler = interrupt::general::get_interrupt_handler(handler_num)) == 0x00) {\
debug::out::printf("Unhandled interrupt %d\n" , handler_num);\
}\
else { \
qword regs_ptr; \
__asm__ ("mov %0 , rsi":"=r"(regs_ptr)); \
handler((struct Registers *)regs_ptr); \
} \
interrupt::controller::interrupt_received(handler_num);\
INTERRUPT_END \
}
I'll walk you through the designs. First, you obviously call the INTERRUPT_START macro(which I will explain below.)
#define INTERRUPT_START \
/* qword regs_ptr; */ /* use RSI register for regs_ptr */ \
IA ("push rax"); \
IA ("mov rax , [rsp+(8*1)]"); /* Save RIP*/ \
IA ("mov [rsp+%c0] , rax"::"i"(-sizeof(struct Registers)+offsetof(struct Registers , rip))); \
IA ("mov rax , [rsp+(8*2)]"); /* Save CS */ \
IA ("mov [rsp+%c0] , rax"::"i"(-sizeof(struct Registers)+offsetof(struct Registers , cs))); \
IA ("mov rax , [rsp+(8*3)]"); /* Save RFlags */ \
IA ("mov [rsp+%c0] , rax"::"i"(-sizeof(struct Registers)+offsetof(struct Registers , rflags))); \
IA ("mov rax , [rsp+(8*4)]"); /* Save the original RSP */ \
IA ("mov [rsp+%c0] , rax"::"i"(-sizeof(struct Registers)+offsetof(struct Registers , rsp))); \
IA ("sub rsp , %c0"::"i"(sizeof(struct Registers))); \
IA ("mov [rsp+%c0] , rbx"::"i"(offsetof(struct Registers , rbx))); \
IA ("mov [rsp+%c0] , rcx"::"i"(offsetof(struct Registers , rcx))); \
IA ("mov [rsp+%c0] , rdx"::"i"(offsetof(struct Registers , rdx))); \
IA ("mov [rsp+%c0] , rdi"::"i"(offsetof(struct Registers , rdi))); \
IA ("mov [rsp+%c0] , rsi"::"i"(offsetof(struct Registers , rsi))); \
IA ("mov [rsp+%c0] , r8"::"i"(offsetof(struct Registers , r8))); \
IA ("mov [rsp+%c0] , r9"::"i"(offsetof(struct Registers , r9))); \
IA ("mov [rsp+%c0] , r10"::"i"(offsetof(struct Registers , r10))); \
IA ("mov [rsp+%c0] , r11"::"i"(offsetof(struct Registers , r11))); \
IA ("mov [rsp+%c0] , r12"::"i"(offsetof(struct Registers , r12))); \
IA ("mov [rsp+%c0] , r13"::"i"(offsetof(struct Registers , r13))); \
IA ("mov [rsp+%c0] , r14"::"i"(offsetof(struct Registers , r14))); \
IA ("mov [rsp+%c0] , r15"::"i"(offsetof(struct Registers , r15))); \
IA ("mov [rsp+%c0] , rbp"::"i"(offsetof(struct Registers , rbp))); \
IA ("mov rax , ss"); \
IA ("mov [rsp+%c0] , rax"::"i"(offsetof(struct Registers , ss))); \
IA ("mov rax , ds"); \
IA ("mov [rsp+%c0] , rax"::"i"(offsetof(struct Registers , ds))); \
IA ("mov rax , es"); \
IA ("mov [rsp+%c0] , rax"::"i"(offsetof(struct Registers , es))); \
IA ("mov rax , fs"); \
IA ("mov [rsp+%c0] , rax"::"i"(offsetof(struct Registers , fs))); \
IA ("mov rax , gs"); \
IA ("mov [rsp+%c0] , rax"::"i"(offsetof(struct Registers , gs))); \
IA ("add rsp , %c0"::"i"(sizeof(struct Registers))); \
IA ("pop rax"); \
IA ("mov [rsp-8+%c0] , rax"::"i"(-sizeof(struct Registers)+offsetof(struct Registers , rax))); \
/* use RSI register for regs_ptr */ \
IA ("mov rsi , rsp"); \
IA ("sub rsi , %c0"::"i"((sizeof(struct Registers)+8))); \
IA ("sub rsp , %c0"::"i"(sizeof(struct Registers)+8)); \
PUSH_REGISTERS
... and I'll be explaining this gigantic monstrosity now..
If you're interested, here's the implementation of offsetof macro. Basically, you set the physical location of the temporary structure to 0x00 and simply return the member's location. Very creative and simple! (It's not my idea)
#define offsetof(s, m) ((max_t)&(((s *)0)->m))
Now that all the interrupts are properly working, it's finally time to really make the ps/2 driver.