Cleanup in elf.c with .bss section clean; adm command mounts cdrom instead of floppy...
[ZeXOS.git] / kernel / lib / x86 / longjmp.c
blob636228df867766e7513e0a19e6855275d135d508
1 /*
2 * ZeX/OS
3 * Copyright (C) 2007 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <system.h>
21 #include <build.h>
23 #ifdef ARCH_i386
25 /* jmp_buf */
26 /*****************************************************************************
27 To use setjmp() and longjmp() for asynchronous (interrupt-driven;
28 pre-emptive) task-switching, we want to enable interrupts simultaneous
29 with jumping to the task. In other words, we want the EFLAGS and EIP
30 registers loaded at the same time.
32 The only instruction that can do this is IRET, which also loads the CS
33 register. Changing CS is done in code that uses far pointers, and it's
34 also done when changing address spaces, and when changing privilege levels.
35 We're not interested in any of those, so just push the current CS value
36 on the stack and let IRET use that.
38 Three distinct stack pointer (ESP) values are used in this routine:
39 - 'Old' or 'current' stack pointer value, which is discarded by
40 this routine (use setjmp() to save it)
41 - ESP is made to point, briefly, to the jmp_buf struct itself
42 - 'New' ESP value; stored in jmp_buf.esp
44 Register values are restored from the jmp_buf as follows:
45 1. Push jmp_buf.eflags, the current CS value, and jmp_buf.eip
46 onto the 'new' stack
47 2. Make ESP point to the jmp_buf struct itself, then use the POPA
48 instruction to pop the 7 general purpose registers (ESP is not
49 loaded by POPA). The use of POPA means that registers in the
50 jmp_buf MUST be stored in the order that POPA expects.
51 (Maybe use MOVs instead, to eliminate this restriction?
52 Might have to rewrite entire function in asm, instead of C.)
53 3. Load ESP with the 'new' stack pointer, from jmp_buf.esp
54 4. Use IRET to pop EIP, CS, and EFLAGS from the 'new' stack
55 5. ???
56 6. Profit! <--- obligatory Slashdot joke
58 This code does NOT save the floating-point state of the CPU. Either:
59 1. Don't use floating point, or
60 2. Don't use floating point in more than one thread, or
61 3. Rewrite this code so it DOES save the floating-point state, or
62 4. Save/restore the floating-point state when entering/leaving
63 the kernel (protected OS only)
64 *****************************************************************************/
66 void longjmp(jmp_buf buf, int ret_val)
68 unsigned *esp;
70 /* make sure return value is not 0 */
71 if(ret_val == 0)
72 ret_val++;
73 /* EAX is used for return values, so store it in jmp_buf.EAX */
74 buf->eax = ret_val;
75 /* get ESP for new stack */
76 esp = (unsigned *)buf->esp;
77 /* push EFLAGS on the new stack */
78 esp--;
79 *esp = buf->eflags;
80 /* push current CS on the new stack */
81 esp--;
82 __asm__ __volatile__(
83 "mov %%cs,%0\n"
84 : "=m"(*esp));
85 /* push EIP on the new stack */
86 esp--;
87 *esp = buf->eip;
88 /* new ESP is 12 bytes lower; update jmp_buf.ESP */
89 buf->esp = (unsigned)esp;
91 /* now, briefly, make the jmp_buf struct our stack */
92 __asm__ __volatile__(
93 "movl %0,%%esp\n"
94 /* ESP now points to 8 general-purpose registers stored in jmp_buf
95 Pop them */
96 "popa\n"
97 "sti\n"
98 /* load new stack pointer from jmp_buf */
99 "movl -20(%%esp),%%esp\n"
100 /* ESP now points to new stack, with the IRET frame (EIP, CS, EFLAGS)
101 we created just above. Pop these registers: */
103 "iret\n"
105 : "m"(buf));
107 #endif