Skip to content

Instantly share code, notes, and snippets.

@mtrudel

mtrudel/int.c Secret

Created December 7, 2021 21:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mtrudel/c29fa60e5b2f3b6fdc46a9e3c65deb20 to your computer and use it in GitHub Desktop.
Save mtrudel/c29fa60e5b2f3b6fdc46a9e3c65deb20 to your computer and use it in GitHub Desktop.
#include <cs452.h>
#include <machine/types.h>
#include <sys/types.h>
#include <machine/cpufunc.h>
#include <machine/pio.h>
#include <machine/psl.h>
#include <machine/pc/isareg.h>
#include <machine/serial.h>
#include "taskmanager.h"
#include "int.h"
#include "kerneldebug.h"
#include "kernelsyscall.h"
#include "kernelidt.h"
#define MAX_INTERRUPTS 256
extern volatile unsigned int kernelESP;
extern KernelGDT GDT;
extern Bindfile bFile;
extern TaskLDT kernelLDT;
volatile unsigned int thrownintnum;
struct segment_descriptor *taskSSDesc;
volatile TaskDescriptorPtr curTask;
extern TaskManager tasker;
extern volatile TaskStateSeg kernelTSS;
TaskStateSeg interruptTSS[MAX_INTERRUPTS];
void int_thirdStageHandler(int intnum) {
if(intnum == INT_DIVIDE_EXCEPT) {
int_handleCriticalException(intnum);
} else if(intnum == INT_DEBUG_EXCEPT) {
taskmanager_signalEvent(&tasker, DEBUG, taskmanager_getActiveTID(&tasker));
} else if(intnum == INT_NMI) {
taskmanager_dumpPS(&tasker);
taskmanager_signalEvent(&tasker, NMI, 0);
while(1);
} else if(intnum == INT_BREAKPOINT) {
taskmanager_signalEvent(&tasker, BREAKPOINT, taskmanager_getActiveTID(&tasker));
} else if(intnum == INT_OVERFLOW_EXCEPT) {
/* Do nothing */
} else if(intnum == INT_OPCODE_EXCEPT) {
int_handleCriticalException(intnum);
} else if(intnum == INT_DOUBLE_EXCEPT) {
int_handleCriticalException(intnum);
} else if(intnum == INT_TSS_EXCEPT) {
int_handleCriticalException(intnum);
} else if(intnum == INT_SEGMENTNP_EXCEPT) {
int_handleCriticalException(intnum);
} else if(intnum == INT_STACK_EXCEPT) {
int_handleCriticalException(intnum);
} else if(intnum == INT_GP_EXCEPT) {
int_handleCriticalException(intnum);
} else if(intnum == INT_TIMER) {
taskmanager_signalEvent(&tasker, TIMER, 0);
} else if(intnum == INT_KEYBOARD) {
taskmanager_signalEvent(&tasker, KEYBOARD, 0);
inb(0x60);
} else if(intnum == INT_SERIAL0) {
taskmanager_signalEvent(&tasker, SERIAL0, 0);
} else if(intnum == INT_SOUND) {
taskmanager_signalEvent(&tasker, SOUND, 0);
} else if(intnum == INT_SERIAL1) {
taskmanager_signalEvent(&tasker, SERIAL1, 0);
taskmanager_signalEvent(&tasker, ETHERNET, 0);
} else if(intnum == INT_RTC) {
taskmanager_signalEvent(&tasker, RTC, 0);
} else if(intnum == INT_ETHERNET) {
taskmanager_signalEvent(&tasker, ETHERNET, 0);
} else if(intnum == INT_SYSCALL) {
curTask = taskmanager_getActiveTask(&tasker);
kernelsyscall_HandleCall(&tasker, &GDT, &bFile, curTask->TSS.esp);
}
}
/*******************************************************************
Here's where the real meat and potatoes of interrupts start
*******************************************************************/
/*********************************************************************
The Real Horror Begins......
**********************************************************************/
/*
Now, a whole whack of first stage interrupt handlers that we can register into the
IDT. Since the addresses of these methods are taken in InitInterrupts, we know that
they will always be generated even though they're never called and aren't declared static
*/
#define RETURNTOUSER \
/* Reset the kernel stack pointer to be the hardwired initial value it was at startup \
We have to do this so that we know where exception CS/EIP/EFLAGS values end up*/\
__asm __volatile("mov %0, %%esp" : : "m" (kernelESP));\
__asm __volatile("iretl");
/*
This is done as a while loop because we have task gates in the ID instead of
interrupts gates. This lets us do all kinds of cool stuff with stacks. The result of all
this is that the interrupt handler is actually stateful between invocations
*/
#define INTINITIALENTRY(intnum) \
while(1) {\
outb(IO_ICU1, 0x20);\
outb(IO_ICU2, 0x20);\
\
/* Now handle the interrupt */\
int_thirdStageHandler(intnum);\
\
curTask = taskmanager_getActiveTask(&tasker);\
\
/* Set this handler's TSS prev task field to be the TSS of the active task */\
interruptTSS[intnum].prevTaskLink = curTask->TSSSelector;\
\
RETURNTOUSER\
}
/* this is called once, when the kernel calls the first task */
void int_jumpToUser(void) {
curTask = taskmanager_getActiveTask(&tasker);
/* Set the kernel's TSS prev task field to be the TSS of the active task */
kernelTSS.prevTaskLink = curTask->TSSSelector;
/* Some grossness that's req'd to fake up the NT bit the first time through */
__asm __volatile("pushf");
__asm __volatile("pop %eax");
__asm __volatile("or %0, %%eax" : : "i" (PSL_NT));
__asm __volatile("push %eax");
__asm __volatile("popf");
RETURNTOUSER
}
void int_handlerINT_DIVIDE_EXCEPT() {
INTINITIALENTRY(INT_DIVIDE_EXCEPT)
}
void int_handlerINT_DEBUG_EXCEPT() {
INTINITIALENTRY(INT_DEBUG_EXCEPT)
}
void int_handlerINT_NMI() {
INTINITIALENTRY(INT_NMI)
}
void int_handlerINT_BREAKPOINT() {
INTINITIALENTRY(INT_BREAKPOINT)
}
void int_handlerINT_OVERFLOW_EXCEPT() {
INTINITIALENTRY(INT_OVERFLOW_EXCEPT)
}
void int_handlerINT_OPCODE_EXCEPT() {
INTINITIALENTRY(INT_OPCODE_EXCEPT)
}
void int_handlerINT_DOUBLE_EXCEPT() {
INTINITIALENTRY(INT_DOUBLE_EXCEPT)
}
void int_handlerINT_TSS_EXCEPT() {
INTINITIALENTRY(INT_TSS_EXCEPT)
}
void int_handlerINT_SEGMENTNP_EXCEPT() {
INTINITIALENTRY(INT_SEGMENTNP_EXCEPT)
}
void int_handlerINT_STACK_EXCEPT() {
INTINITIALENTRY(INT_STACK_EXCEPT)
}
void int_handlerINT_GP_EXCEPT() {
INTINITIALENTRY(INT_GP_EXCEPT)
}
void int_handlerINT_TIMER() {
INTINITIALENTRY(INT_TIMER)
}
void int_handlerINT_KEYBOARD() {
INTINITIALENTRY(INT_KEYBOARD)
}
void int_handlerINT_SERIAL0() {
INTINITIALENTRY(INT_SERIAL0)
}
void int_handlerINT_SOUND() {
INTINITIALENTRY(INT_SOUND)
}
void int_handlerINT_SERIAL1() {
INTINITIALENTRY(INT_SERIAL1)
}
void int_handlerINT_RTC() {
INTINITIALENTRY(INT_RTC)
}
void int_handlerINT_ETHERNET() {
INTINITIALENTRY(INT_ETHERNET)
}
void int_handlerINT_SYSCALL() {
INTINITIALENTRY(INT_SYSCALL)
}
/* This method copied verbatim from course notes */
void int_maskIRQ(u_char irq, int flag) {
u_int port = IO_ICU1+1;
u_char val;
if (irq >= 8) {
port = IO_ICU2+1;
irq -= 8;
}
val = inb(port);
if (flag) val |= (1<<irq);
else val &= ~(1<<irq);
outb(port, val);
}
/* This method is documented in the header */
void int_InitInterrupts(KernelGDTPtr GDT, unsigned int ESP) {
loadIdt();
int_AddNewInterruptHandler(INT_DIVIDE_EXCEPT, GDT, int_handlerINT_DIVIDE_EXCEPT, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_DEBUG_EXCEPT, GDT, int_handlerINT_DEBUG_EXCEPT, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_NMI, GDT, int_handlerINT_NMI, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_BREAKPOINT, GDT, int_handlerINT_BREAKPOINT, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_OVERFLOW_EXCEPT, GDT, int_handlerINT_OVERFLOW_EXCEPT, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_OPCODE_EXCEPT, GDT, int_handlerINT_OPCODE_EXCEPT, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_DOUBLE_EXCEPT, GDT, int_handlerINT_DOUBLE_EXCEPT, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_TSS_EXCEPT, GDT, int_handlerINT_TSS_EXCEPT, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_SEGMENTNP_EXCEPT, GDT, int_handlerINT_SEGMENTNP_EXCEPT, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_STACK_EXCEPT, GDT, int_handlerINT_STACK_EXCEPT, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_GP_EXCEPT, GDT, int_handlerINT_GP_EXCEPT, ESP, KERNEL_PRIV_LEVEL);
/* Hardware interrupts */
int_AddNewInterruptHandler(INT_TIMER, GDT, int_handlerINT_TIMER, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_KEYBOARD, GDT, int_handlerINT_KEYBOARD, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_SERIAL0, GDT, int_handlerINT_SERIAL0, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_SOUND, GDT, int_handlerINT_SOUND, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_SERIAL1, GDT, int_handlerINT_SERIAL1, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_RTC, GDT, int_handlerINT_RTC, ESP, KERNEL_PRIV_LEVEL);
int_AddNewInterruptHandler(INT_ETHERNET, GDT, int_handlerINT_ETHERNET, ESP, KERNEL_PRIV_LEVEL);
/* Syscall interrupts */
int_AddNewInterruptHandler(INT_SYSCALL, GDT, int_handlerINT_SYSCALL, ESP, USER_TASK_PRIV_LEVEL);
/* Unmask the PIT */
int_maskIRQ(0, 0);
/* Unmask the keyboard */
int_maskIRQ(1, 0);
/* Unmask serial0 */
int_maskIRQ(4, 0);
/* Unmask serial1 */
int_maskIRQ(3, 0);
/* Unmask ethernet */
int_maskIRQ(10, 0);
}
/* this adds a taskgate into the IDT, obviating the need for the loadIDT in libcs452 */
void int_AddNewInterruptHandler(unsigned char intValue, KernelGDTPtr GDT, void (*func)(), unsigned int ESP, unsigned char privlevel) {
unsigned short TSSSel;
TaskStateSegPtr TSS = &(interruptTSS[intValue]);
TSS->ss0 = 0x38;
TSS->esp0 = ESP;
TSS->eip = (unsigned int) func;
TSS->eflags = PSL_MBO;
TSS->esp = ESP;
TSS->ss = TSS->ds = TSS->es = TSS->fs = TSS->gs = 0x38;
TSS->cs = 0x30;
TSS->ldtSegSelector = kernelLDT.selector;
TSSSel = kernelgdt_AddSegment(GDT, (unsigned int) TSS + baseDS, sizeof(TaskStateSeg) - 1, TSS_TYPE_AVAIL, KERNEL_PRIV_LEVEL);
kernelidt_AddTaskGateToIDT(intValue, TSSSel, privlevel);
}
int int_GetThrower(int intnum) {
unsigned short throwerTSSselector;
struct segment_descriptor *throwerTSSdesc;
TaskStateSegPtr throwerTSS;
int result;
if (intnum < 0 || intnum >= MAX_INTERRUPTS) return -1;
throwerTSSselector = interruptTSS[intnum].prevTaskLink;
throwerTSSdesc = GDT.table + (throwerTSSselector>>3);
throwerTSS = (TaskStateSegPtr)((throwerTSSdesc->sd_hibase << 24) + throwerTSSdesc->sd_lobase);
result = ((unsigned int)throwerTSS - (unsigned int)tasker.tasks - baseDS) / sizeof(TaskDescriptor);
return taskmanager_getIDByTaskDescriptor(&tasker, tasker.tasks + result);
}
void int_handleCriticalException(int intnum) {
int naughtytask = int_GetThrower(intnum);
if (naughtytask == 0) {
kprintf("A critical kernel side exception occured (type 0x%x). Halting system!\n", intnum);
while(1);
} else {
kprintf("Task %s (taskID %08x) caused a critical exception (type 0x%x). Killing it.\n", taskmanager_getTaskDescriptorByID(&tasker, naughtytask)->name, naughtytask, intnum);
taskmanager_destroyTaskByTID(&tasker, naughtytask);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment