Closed StrikerX3 closed 6 years ago
I came up with this high-level approach to hardware interrupts in the emulator:
Cpu
object.Cpu::RunImpl
/ Cpu::StepImpl
exitsCpu::Run()
/ Cpu::Step
notices that CPU emulation was interrupted and that there is a pending interrupt request. It clears the pending interrupt and returns with EXIT_INTERRUPT
and the interrupt vectorXbox::RunCpu
invokes the interrupt handlerWe must guarantee thread safety because interrupts will be performed by other threads. In particular, the pending interrupt request variable and the CPU emulation state must be synchronized. The CPU emulator must not be allowed to run while the interrupt handler is running, and vice-versa. Also, only one interrupt handler may execute at any given point in time. This correlates to the fact that the interrupt handlers are executed by the CPU and we cannot have the same logical thread perform two tasks simultaneously.
The interrupt handler must be aware of the three types of interrupts that work in different ways: interrupt gates, trap gates and task gates. (There are more types, but the Xbox only uses these three in 32-bit mode.) These are set up in the IDT, which is not done by OpenXBOX at the moment. Before invoking the actual handler function, the interrupt handler must prepare the CPU context, which may involve pushing registers onto the stack or even switching tasks using the TSS, then invoke the handler function, and finally complete the interrupt invocation according to the gate type.
As it turns out, trying to suspend the host thread when it is not running the CPU emulator causes a lot of problems. For instance, threads can be suspended while they are in the middle of printf, in which case, if the interrupt handler thread tries to printf something, it will hang.
Considering that kernel function calls are supposed to be quick, I'm going to handle interrupts exclusively on the Cpu
class instead of doing hackery with host threads. That way I can guarantee that the threads aren't doing anything crazy.
Commit ab4ac27 has the basic interrupt framework implemented. Now we need to setup the IDT, prepare the CPU context and implement the handlers.
As a bonus, we have our first piece of hardware generating interrupts on OpenXBOX: the system clock.
Commit e710d96 implements the last few details of hardware interrupts, as well as the system clock interrupt handler. With logging enabled, it really slows down emulation, but without logging, it's still pretty fast.
Unicorn (the only CPU emulator used on OpenXBOX) doesn't handle interrupts on its own. We'll need to emulate them while leaving the possibility of taking advantage of interrupt emulation features of more capable CPU emulators.
OSDev's page on Interrupts and the Interrupt Descriptor Table are good starting points for research. The Intel® 64 and IA-32 Architectures Software Developer Manuals are a great resource for understanding interrupts in depth.
This will serve as the basis for emulating certain pieces of Xbox hardware such as the System Clock that plays a role in thread switching implemented on #1.