2 ; Low level interrupt/thread handling code for GeekOS.
3 ; Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
4 ; Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
7 ; This is free software. You are permitted to use,
8 ; redistribute, and modify it as specified in the file "COPYING".
10 ; This is 32 bit code to be linked into the kernel.
11 ; It defines low level interrupt handler entry points that we'll use
12 ; to populate the IDT. It also contains the interrupt handling
13 ; and thread context switch code.
22 ; ----------------------------------------------------------------------
24 ; ----------------------------------------------------------------------
26 ; This is the size of the Interrupt_State struct in int.h
27 INTERRUPT_STATE_SIZE equ 64
29 ; Save registers prior to calling a handler function.
30 ; This must be kept up to date with:
31 ; - Interrupt_State struct in int.h
32 ; - Setup_Initial_Thread_Context() in kthread.c
33 %macro Save_Registers 0
47 ; Restore registers and clean up the stack after calling a handler function
48 ; (i.e., just before we return from the interrupt via an iret instruction).
49 %macro Restore_Registers 0
61 add esp, 8 ; skip int num and error code
64 ; Code to activate a new user context (if necessary), before returning
65 ; to executing a thread. Should be called just before restoring
66 ; registers (because the interrupt context is used).
67 %macro Activate_User_Context 0
68 ; If the new thread has a user context which is not the current
70 push esp ; Interrupt_State pointer
71 push dword [g_currentThread] ; Kernel_Thread pointer
72 ; call Switch_To_User_Context
73 add esp, 8 ; clear 2 arguments
76 ; Number of bytes between the top of the stack and
77 ; the interrupt number after the general-purpose and segment
78 ; registers have been saved.
81 ; Template for entry point code for interrupts that have
82 ; an explicit processor-generated error code.
83 ; The argument is the interrupt number.
86 push dword %1 ; push interrupt number
87 jmp Handle_Interrupt ; jump to common handler
90 ; Template for entry point code for interrupts that do not
91 ; generate an explicit error code. We push a dummy error
92 ; code on the stack, so the stack layout is the same
96 push dword 0 ; fake error code
97 push dword %1 ; push interrupt number
98 jmp Handle_Interrupt ; jump to common handler
102 ; ----------------------------------------------------------------------
103 ; Symbol imports and exports
104 ; ----------------------------------------------------------------------
106 ; This symbol is defined in idt.c, and is a table of addresses
107 ; of C handler functions for interrupts.
108 IMPORT g_interruptTable
110 ; Global variable pointing to context struct for current thread.
111 IMPORT g_currentThread
113 ; Set to non-zero when we need to choose a new thread
114 ; in the interrupt return code.
115 IMPORT g_needReschedule
117 ; Set to non-zero when preemption is disabled.
118 IMPORT g_preemptionDisabled
120 ; This is the function that returns the next runnable thread.
121 IMPORT Get_Next_Runnable
123 ; Function to put a thread on the run queue.
126 ; Function to activate a new user context (if needed).
127 IMPORT Switch_To_User_Context
130 IMPORT SerialPrintHex
133 ; Sizes of interrupt handler entry points for interrupts with
134 ; and without error codes. The code in idt.c uses this
135 ; information to infer the layout of the table of interrupt
136 ; handler entry points, without needing a separate linker
137 ; symbol for each one (which is quite tedious to type :-)
138 EXPORT g_handlerSizeNoErr
139 EXPORT g_handlerSizeErr
141 ; Simple functions to load the IDTR, GDTR, and LDTR.
146 ; Beginning and end of the table of interrupt entry points.
147 EXPORT g_entryPointTableStart
148 EXPORT g_entryPointTableEnd
150 ; Thread context switch function.
151 EXPORT Switch_To_Thread
153 ; Return current value of eflags register.
154 EXPORT Get_Current_EFLAGS
156 ; Return the return address
166 ; Virtual memory support.
188 ; ----------------------------------------------------------------------
190 ; ----------------------------------------------------------------------
194 ; Load IDTR with 6-byte pointer whose address is passed as
202 ; Load the GDTR with 6-byte pointer whose address is
203 ; passed as the parameter. Assumes that interrupts
209 ; Reload segment registers
220 ; Load the LDT whose selector is passed as a parameter.
229 ; load crt3 with the passed page directory pointer
230 ; enable paging bit in cr2
255 ; load cr3 with the passed page directory pointer
263 ; Get the current PDBR.
264 ; This is useful for lazily switching address spaces;
265 ; only switch if the new thread has a different address space.
273 ; Flush TLB - just need to re-load cr3 to force this to happen
284 ; cpuid_edx - return the edx register from cpuid
306 ; cpuid_ecx - return the ecx register from cpuid
327 ; cpuid_eax - return the eax register from cpuid
347 ; Set_MSR - Set the value of a given MSR
365 ; Get_MSR - Get the value of a given MSR
366 ; void Get_MSR(int MSR, void * high_byte, void * low_byte);
416 ; Common interrupt handling code.
417 ; Save registers, call C handler function,
418 ; possibly choose a new thread to run, restore
419 ; registers, return from the interrupt.
422 ; Save registers (general purpose and segment)
425 ; Ensure that we're using the kernel data segment
430 ; Get the address of the C handler function from the
431 ; table of handler functions.
432 mov eax, g_interruptTable ; get address of handler table
433 mov esi, [esp+REG_SKIP] ; get interrupt number
434 mov ebx, [eax+esi*4] ; get address of handler function
437 ; call SerialPrintHex
448 ; The argument passed is a pointer to an Interrupt_State struct,
449 ; which describes the stack layout for all interrupts.
452 add esp, 4 ; clear 1 argument
454 ; If preemption is disabled, then the current thread
456 cmp [g_preemptionDisabled], dword 0
459 ; See if we need to choose a new thread to run.
460 cmp [g_needReschedule], dword 0
463 ; Put current thread back on the run queue
464 push dword [g_currentThread]
466 add esp, 4 ; clear 1 argument
468 ; Save stack pointer in current thread context, and
469 ; clear numTicks field.
470 mov eax, [g_currentThread]
471 mov [eax+0], esp ; esp field
472 mov [eax+4], dword 0 ; numTicks field
474 ; Pick a new thread to run, and switch to its stack
475 call Get_Next_Runnable
476 mov [g_currentThread], eax
477 mov esp, [eax+0] ; esp field
479 ; Clear "need reschedule" flag
480 mov [g_needReschedule], dword 0
501 ; mov eax, [ebp + ecx]
504 ; call SerialPrintHex
520 ; Activate the user context, if necessary.
521 Activate_User_Context
531 ; call SerialPrintHex
541 ; Return from the interrupt.
544 ; ----------------------------------------------------------------------
546 ; Save context of currently executing thread, and activate
547 ; the thread whose context object is passed as a parameter.
550 ; - ptr to Kernel_Thread whose state should be restored and made active
553 ; Called with interrupts disabled.
554 ; This must be kept up to date with definition of Kernel_Thread
555 ; struct, in kthread.h.
556 ; ----------------------------------------------------------------------
559 ; Modify the stack to allow a later return via an iret instruction.
560 ; We start with a stack that looks like this:
563 ; esp --> return addr
565 ; We change it to look like this:
570 ; esp --> return addr
573 mov eax, [esp+4] ; get return address
574 mov [esp-4], eax ; move return addr down 8 bytes from orig loc
575 add esp, 8 ; move stack ptr up
576 pushfd ; put eflags where return address was
577 mov eax, [esp-4] ; restore saved value of eax
578 push dword KERNEL_CS ; push cs selector
579 sub esp, 4 ; point stack ptr at return address
581 ; Push fake error code and interrupt number
585 ; Save general purpose registers.
588 ; Save stack pointer in the thread context struct (at offset 0).
589 mov eax, [g_currentThread]
592 ; Clear numTicks field in thread context, since this
593 ; thread is being suspended.
596 ; Load the pointer to the new thread context into eax.
597 ; We skip over the Interrupt_State struct on the stack to
599 mov eax, [esp+INTERRUPT_STATE_SIZE]
601 ; Make the new thread current, and switch to its stack.
602 mov [g_currentThread], eax
605 ; Activate the user context, if necessary.
606 Activate_User_Context
608 ; Restore general purpose and segment registers, and clear interrupt
609 ; number and error code.
612 ; We'll return to the place where the thread was
616 ; Return current contents of eflags register.
620 pop eax ; pop contents into eax
625 ; Return the eip of the next instruction after the caller
631 ; Return the current esp in the procedure
637 ; Return the current ebp in the procedure
645 ; ----------------------------------------------------------------------
646 ; Generate interrupt-specific entry points for all interrupts.
647 ; We also define symbols to indicate the extend of the table
648 ; of entry points, and the size of individual entry points.
649 ; ----------------------------------------------------------------------
651 g_entryPointTableStart:
653 ; Handlers for processor-generated exceptions, as defined by
661 Int_No_Err 2 ; FIXME: not described in 486 manual
672 Int_No_Err 9 ; FIXME: not described in 486 manual
678 Int_No_Err 15 ; FIXME: not described in 486 manual
682 ; The remaining interrupts (18 - 255) do not have error codes.
683 ; We can generate them all in one go with nasm's %rep construct.
687 %assign intNum intNum+1
691 g_entryPointTableEnd:
695 ; Exported symbols defining the size of handler entry points
696 ; (both with and without error codes).
698 g_handlerSizeNoErr: dd (After_No_Err - Before_No_Err)
700 g_handlerSizeErr: dd (After_Err - Before_Err)