/* TMHalAsm.S, Copyright (c) by Lukas Ruf, lr@lpr.ch, Swiss Federal Institute of Technology, Computer Engineering and Networks Laboratory. TOPSY -- A Teachable Operating System. Implementation of a tiny and simple micro kernel for teaching purposes. For further information, please visit http://www.tik.ee.ethz.ch/~topsy This software is provided under the terms of the GNU General Public Licence. A full copy of the GNU GPL is provided in the file COPYING found in the development root of Topsy. */ /* File: $Source: /usr/drwho/vault/cvs/topsy/Topsy/Threads/ia32/TMHalAsm.S,v $ Author(s): Lukas Ruf, lr@lpr.ch Affiliation: ETH Zuerich, TIK Version: $Revision: 1.2 $ Creation Date: Last Date of Change: $Date: 1999/12/13 21:48:34 $ by: $Author: ruf $ */ #include "asm.h" #include "cpu.h" #include "MemoryLayout.h" #include "SyscallMsg.h" /* define SASM to make only "things" for assembler visible */ #define __SASM__ #include "TMHalAsm.h" #include "Exception.h" #undef __SASM__ /* Definitions for Interrupt Handling. */ #define TRAP(_irqno_) \ pushal ; \ nop ; \ pushl $(_irqno_) ; \ jmp __general_ExceptionHandler #define IRQ(_name_,_irqno_) \ .align 4 ; \ .globl _name_ ; _name_: ; \ cli ; \ pushl $0 ; \ TRAP(_irqno_) /* Definitions for Execption Handling. */ /* Define the entry for IDT Vectorization :-) Please remark: as some exception serve with an error code, there is no generalisation possible :-( */ #define IDTVEC(_name_) .align 4 ; .globl _name_ ; _name_: .extern _INTHandler /* General constants */ #define BIT_UNSET 0 #define BIT_SET 1 .section .sasm .align 4,0x9090 /* Save context of current thread. All registers but k0, k1, AT and * zero are saved in the ProcContext structure given by the first * argument to saveContext(), thus register a0. * FRAME(saveContext) !!!!!***i386***!!!!! * Note:SaveContext is in i386 implicitely called by the low level IRQ-Handlers. */ /**restoreContext()************************************************************/ /* restoreContext(ProcContextPtr Context); * Ideally this should be called FAKE STACK. Because in i386 environment * the context of a thread is restored by modifying the stack. * Forget all register savings. Proceed as even you were alone on the world.. * Do not take care about stack environment.... it is all saved on the * kernel stack via pushall ... so load the stack pointer from Context.tf_xsp * and launch a popall... afterwards continue with an iret (interrupt return) * */ FRAME(restoreContext) ENTER /* Execute an ENTER to Procedure entry as to provide a stack * frame as common in C/Topsy-Assembler */ /* the kernel stack includes all flags and common purpose registers */ cli /* disable interrupt execution -- we modify the stack directly. Even if Interrupts are turned of, make this sure */ movl ARG1,%eax /* store the address of ProcContext in eax */ movl %eax,%esp /* let esp directly point to this context. */ /* just for debuggin */ cmpl $0x8000,%esp /* something went wrong. Stack may never point into codesegment */ ja __contRestCtx movl $BOOTSTACKTOP,%esp /* set exception stack by hand */ pushl %eax /* save eax to show, where stack started */ call DumpStack hlt /* and abort processing */ __contRestCtx: popl %gs /* restore these two segment selectors */ popl %fs popl %es /* restore Extra Segment Selector */ popl %ds /* restore Data Segment Selector */ /* NOTE: ESP points now to the trapno !! */ addl $4,%esp /* remove IRQ no. from stack -- we do not use it anymore :-) */ popal /* restore all general purpose registers */ addl $4,%esp /* remove err no. too */ /* ########################### */ /* NOTE: ESP points now to EIP */ /* ########################### */ /* Now, the real magic has to be done. Pray God not to make an error :-) */ /* No modifications were made to the stack structure provided by the Process Context. */ cmpl $gcKCSEL, 4(%esp) /* compare the provided Code Selector */ jne _Nothing_Has_To_Be_Done /* So, become wizard :-), please take a look at ~/Topsy/Documentation/IPC/Interrupt.dok as I do not like to explain the whole magic inside this file :-) */ pushl $gcKDSEL /* make sure the stack segment equals to the */ popl %ss /* kernel data segment */ /* ######################## */ /* NOTE: ESP points to EIP */ /* ######################## */ /* save %edi to xtemp: no mutex or semaphore is required as all ints are disabled --> directly acces all */ movl %edi,%ss:xtemp /* note: here we are in kernel data segment ! */ movl 12(%esp),%edi /* %edi = tf_esp */ /* make room for the temporarely setting of EIP, CS and EFLAGS on the stack of the thread to be started */ subl $12,%edi /* use %eax to transfer: this f...ing gas does not understand all newely implemented features of the ix86-architecture as memory to memory moves */ movl %eax,%ss:xtemp2 /* we can do this: SS is Kernel Data Segment */ /* ############################################# */ /* NOTE: ESP points to DS (pushed after EIP :-) */ /* ############################################# */ /* %edi points now to space where EIP,CS and EFLAGS have to be moved to */ movl 0(%esp),%eax movl %eax,%ss:0(%edi) /* transfer EIP */ movl 4(%esp),%eax movl %eax,%ss:4(%edi) /* transfer CS */ movl 8(%esp),%eax movl %eax,%ss:8(%edi) /* transfer EFLAGS */ /* restore the previous saved %eax from the kernel data segment */ movl %ss:xtemp2,%eax /* remember: SS already set */ movl %edi,%esp /* now forget the old stack, move to the new */ movl %ss:xtemp,%edi /* restore the orignal %edi */ /* and now: go to heaven or hell */ _Nothing_Has_To_Be_Done: iret /* and go either to heaven or hell !! */ /**End of restoreContext()*****************************************************/ /* This code performs transparently to the user a tmThreadExit() when * the main thread routine returns */ FRAME(automaticThreadExit) movl $endAutomaticThreadExit,%eax /* Calculate size of */ movl $automaticThreadExit,%ebx /* automaticThreadExit()*/ subl %ebx,%eax /* calculate it by subtraction */ addl %esp,%eax /* add the stack pointer to it, as relative to stack=data=code segment */ addl $8,%eax /* skip both arguments on stack laying before the automatic ThreadExit */ movl %eax,%esi /* MESSAGE ADDRESS */ movl $dSYSCALL_SEND_OP,%eax /* SYSCALL TO BE PERFORMED */ movl $0,%ebx /* HOW SHOULD THE SENDER ID BE DETERMINED ???? */ SYSCALL /* and launch it !!!! */ FRAME(endAutomaticThreadExit) /* Either syscallExceptionHandler() and hwExceptionHandler() are both implemented using the procedure _INTHandler(). This function disposes control to the appropriate C-function directly. I chose this implementation to try developping a cleaerer process flow from user to kernel mode and vice versa. */ FRAME(syscallExceptionHandler) FRAME(hwExceptionHandler) /*Lowest Level Interrupt Handling*********************************************/ /* Define the Exception Handlers */ IDTVEC(__NEx_00) /* Divide Error */ pushl $0 /* dummy error Code */ TRAP(T_Ex_00) /* call trap handler */ IDTVEC(__NEx_01) /* Debug Exception */ pushl $0 /* dummy error Code */ TRAP(T_Ex_01) /* call trap handler */ IDTVEC(__NEx_02) /* Nonmaskable Interrupt */ pushl $0 /* dummy error Code */ TRAP(T_Ex_02) /* call trap handler */ IDTVEC(__NEx_03) /* Breakpoint (one byte INT 3 Instruction) */ pushl $0 /* dummy error Code */ TRAP(T_Ex_03) /* call trap handler */ IDTVEC(__NEx_04) /* Overflow (INTO) */ pushl $0 /* dummy error Code */ TRAP(T_Ex_04) /* call trap handler */ IDTVEC(__NEx_05) /* Bounds check (BOUND) */ pushl $0 /* dummy error Code */ TRAP(T_Ex_05) /* call trap handler */ IDTVEC(__NEx_06) /* Invalid Opcode */ pushl $0 /* dummy error Code */ TRAP(T_Ex_06) /* call trap handler */ IDTVEC(__NEx_07) /* Coprocessor not available */ pushl $0 /* dummy error Code */ TRAP(T_Ex_07) /* call trap handler */ IDTVEC(__NEx_08) /* Double fault (Error Code) */ TRAP(T_Ex_08) /* call trap handler */ IDTVEC(__NEx_09) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_09) /* call trap handler */ IDTVEC(__NEx_0A) /* Invalid TSS (Error Code) */ TRAP(T_Ex_0A) /* call trap handler */ IDTVEC(__NEx_0B) /* Segment not present (Error Code) */ TRAP(T_Ex_0B) /* call trap handler */ IDTVEC(__NEx_0C) /* Stack exception (Error Code) */ TRAP(T_Ex_0C) /* call trap handler */ IDTVEC(__NEx_0D) /* General Protection (Error Code) */ TRAP(T_Ex_0D) /* call trap handler */ IDTVEC(__NEx_0E) /* Page Fault (Error Code) */ TRAP(T_Ex_0E) /* call trap handler */ IDTVEC(__NEx_0F) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_0F) /* call trap handler */ IDTVEC(__NEx_10) /* Coprocessor error */ pushl $0 /* dummy error Code */ TRAP(T_Ex_10) /* call trap handler */ IDTVEC(__NEx_11) /* Alignment check (Error Code) */ TRAP(T_Ex_11) /* call trap handler */ IDTVEC(__NEx_12) /* Machine check (Error Code) */ TRAP(T_Ex_12) /* call trap handler */ IDTVEC(__NEx_13) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_13) /* call trap handler */ IDTVEC(__NEx_14) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_14) /* call trap handler */ IDTVEC(__NEx_15) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_15) /* call trap handler */ IDTVEC(__NEx_16) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_16) /* call trap handler */ IDTVEC(__NEx_17) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_17) /* call trap handler */ IDTVEC(__NEx_18) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_18) /* call trap handler */ IDTVEC(__NEx_19) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_19) /* call trap handler */ IDTVEC(__NEx_1A) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_1A) /* call trap handler */ IDTVEC(__NEx_1B) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_1B) /* call trap handler */ IDTVEC(__NEx_1C) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_1C) /* call trap handler */ IDTVEC(__NEx_1D) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_1D) /* call trap handler */ IDTVEC(__NEx_1E) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_1E) /* call trap handler */ IDTVEC(__NEx_1F) /* reserved */ pushl $0 /* dummy error Code */ TRAP(T_Ex_1F) /* call trap handler */ /* Define the Interrupt Handlers. */ IRQ(__NIR_00,T_IR_00) IRQ(__NIR_01,T_IR_01) IRQ(__NIR_02,T_IR_02) IRQ(__NIR_03,T_IR_03) IRQ(__NIR_04,T_IR_04) IRQ(__NIR_05,T_IR_05) IRQ(__NIR_06,T_IR_06) IRQ(__NIR_07,T_IR_07) IRQ(__NIR_08,T_IR_08) IRQ(__NIR_09,T_IR_09) IRQ(__NIR_0A,T_IR_0A) IRQ(__NIR_0B,T_IR_0B) IRQ(__NIR_0C,T_IR_0C) IRQ(__NIR_0D,T_IR_0D) IRQ(__NIR_0E,T_IR_0E) IRQ(__NIR_0F,T_IR_0F) IRQ(__INT_30,T_IS_30) /* Software Interrupt !! equals to "SYSCALL" :-) */ IRQ(__INT_31,T_IS_31) /* Software Interrupt: Block all other */ __general_ExceptionHandler: pushl %ds pushl %es pushl %fs pushl %gs movl $gcKDSEL,%eax movw %ax,%ds movw %ax,%es pushl %esp /* provide even current stack pointer for testing */ call _INTHandler /* I suppose _INTHandler() always calls */ /* restoreContext() :-) */ cli /* If this _INTHandler() ever should */ hlt /* return, hang the machine: I'm human. */ /*============================================================================= void _endOfIRQ(uchar pIRQNo); Sends End Of Interrupt to PICU (or Both) based on IRQ number (0-15) If IRQnum is 0-7 then we send to 1, else we send to 1 then 2. */ FRAME(__endOf_IRQ) ENTER pushl %eax movl 8(%ebp),%eax /*get irq number (0-15) */ movb %al,%ah movb $0x20,%al outb %al,$PIC1_IO /* send EOI to PIC 1 */ cmpb $7,%ah /* pic1 only? */ jbe eoi00 /* yes */ outb %al,$PIC2_IO /* send to PIC 2 also */ eoi00: popl %eax LEAVE /*=============================================== void _disableIRQ(uchar pIRQNo) masks the IRQ number specified (0-15). The proper PICU is selected based on the IRQ number. */ FRAME(__disable_IRQ) ENTER pushfl cli pushl %eax pushl %ecx pushl %edx movl $1,%eax movl 8(%ebp),%ecx /* get irq number (0-15) */ andl $0x0F,%ecx /* (0-15) */ shll %cl,%eax /* set the bit for the irq (0-7) or (8-15) */ andb %al,%al jz dirq2 movl $PIC1_MASK,%edx jmp dirq3 dirq2: movl $PIC2_MASK,%edx movb %ah,%al dirq3: movb %al,%ah inb %dx,%al pushl %eax popl %eax orb %ah,%al outb %al,%dx popl %edx popl %ecx popl %eax popfl /* give em their flags back */ LEAVE /*=============================================== void _enableIRQ(uchar pIRQNo) UNmasks the IRQ number specified (0-15). The proper PICU is selected based on the IRQ number. */ FRAME(__enable_IRQ) ENTER pushfl cli /* previous state is reset on popfl */ pushl %eax pushl %ecx pushl %edx movl $1,%eax movl 8(%ebp),%ecx /* get irq number (0-15) */ andl $0x0F,%ecx /* (0-15 only) */ shll %cl,%eax /* set the bit for the irq (0-7) */ andb %al,%al jz eirq2 movl $PIC1_MASK,%edx jmp eirq3 eirq2: movl $PIC2_MASK,%edx movb %ah,%al eirq3: movb %al,%ah inb %dx,%al pushl %eax popl %eax notb %ah andb %ah,%al outb %al,%dx popl %edx popl %ecx popl %eax popfl LEAVE /* Preserve static temporary space in the kernel data segment for modifying the thread stack if a kernel thread has to be started */ .data .align 4,0x9090 xtemp: .long 0x0 xtemp2: .long 0x0