more ppc work. Added most low level cpu operations
[newos.git] / kernel / arch / i386 / arch_int.c
blob66a4e048a0ba321afa133a130293f1919cc15929
1 /*
2 ** Copyright 2001, Travis Geiselbrecht. All rights reserved.
3 ** Distributed under the terms of the NewOS License.
4 */
5 #include <kernel/kernel.h>
6 #include <kernel/vm.h>
7 #include <kernel/debug.h>
8 #include <kernel/console.h>
9 #include <kernel/int.h>
10 #include <kernel/thread.h>
11 #include <kernel/smp.h>
12 #include <kernel/syscalls.h>
13 #include <kernel/vm_priv.h>
15 #include <kernel/arch/cpu.h>
16 #include <kernel/arch/int.h>
17 #include <kernel/arch/faults.h>
18 #include <kernel/arch/vm.h>
19 #include <kernel/arch/smp.h>
21 #include <kernel/arch/i386/interrupts.h>
22 #include <kernel/arch/i386/faults.h>
24 #include <boot/stage2.h>
26 #include <string.h>
28 #define MAX_ARGS 16
30 #define SYSCALL_VECTOR 99
32 static desc_table *idt = NULL;
34 static void interrupt_ack(int n)
36 if(n >= 0x20 && n < 0x30) {
37 // 8239 controlled interrupt
38 if(n > 0x27)
39 out8(0x20, 0xa0); // EOI to pic 2
40 out8(0x20, 0x20); // EOI to pic 1
44 static void _set_gate(desc_table *gate_addr, unsigned int addr_t, int type, int dpl)
46 unsigned int gate1; // first byte of gate desc
47 unsigned int gate2; // second byte of gate desc
49 gate1 = (KERNEL_CODE_SEG << 16) | (0x0000ffff & addr_t);
50 gate2 = (0xffff0000 & addr_t) | 0x8000 | (dpl << 13) | (type << 8);
52 gate_addr->a = gate1;
53 gate_addr->b = gate2;
56 void arch_int_enable_io_interrupt(int irq)
58 if(irq < 0x20 || irq >= 0x30) return;
59 irq -= 0x20;
60 // if this is a external interrupt via 8239, enable it here
61 if (irq < 8)
62 out8(in8(0x21) & ~(1 << irq), 0x21);
63 else
64 out8(in8(0xa1) & ~(1 << (irq - 8)), 0xa1);
67 void arch_int_disable_io_interrupt(int irq)
69 if(irq < 0x20 || irq >= 0x30) return;
70 irq -= 0x20;
71 // if this is a external interrupt via 8239, disable it here
72 if (irq < 8)
73 out8(in8(0x21) | (1 << irq), 0x21);
74 else
75 out8(in8(0xa1) | (1 << (irq - 8)), 0xa1);
78 void i386_set_task_gate(int n, uint32 seg)
80 idt[n].a = (seg << 16); // tss segment in 31:16
81 idt[n].b = 0x8000 | (0 << 13) | (0x5 << 8); // present, dpl 0, type 5
84 static void set_intr_gate(int n, void *addr_t)
86 _set_gate(&idt[n], (unsigned int)addr_t, 14, 0);
89 // unused
90 #if 0
91 static void set_trap_gate(int n, void *addr_t)
93 _set_gate(&idt[n], (unsigned int)addr_t, 15, 0);
95 #endif
97 static void set_system_gate(int n, void *addr_t)
99 _set_gate(&idt[n], (unsigned int)addr_t, 15, 3);
102 void arch_int_enable_interrupts(void)
104 asm("sti");
107 void arch_int_disable_interrupts(void)
109 asm("cli");
112 bool arch_int_are_interrupts_enabled(void)
114 int flags;
116 asm("pushfl;\n"
117 "popl %0;\n" : "=g" (flags));
118 return flags & 0x200 ? 1 : 0;
121 void i386_handle_trap(struct iframe frame); /* keep the compiler happy, this function must be called only from assembly */
122 void i386_handle_trap(struct iframe frame)
124 int ret = INT_NO_RESCHEDULE;
125 bool adjust_int_disable_count = false;
126 struct thread *t = thread_get_current_thread();
128 if(t) {
129 i386_push_iframe(t, &frame);
130 if(frame.vector != SYSCALL_VECTOR) {
131 adjust_int_disable_count = true;
132 t->int_disable_level++; // make it look like the ints were disabled
136 // if(frame.vector != 0x20)
137 // dprintf("i386_handle_trap: vector 0x%x, ip 0x%x, cpu %d\n", frame.vector, frame.eip, smp_get_current_cpu());
138 switch(frame.vector) {
139 case 8:
140 ret = i386_double_fault(frame.error_code);
141 break;
142 case 13:
143 ret = i386_general_protection_fault(frame.error_code);
144 break;
145 case 14: {
146 unsigned int cr2;
147 addr_t newip;
149 asm ("movl %%cr2, %0" : "=r" (cr2) );
151 if(!kernel_startup && (frame.flags & 0x200) == 0) {
152 // ints are were disabled when the page fault was taken, that is very bad
153 panic("i386_handle_trap: page fault at 0x%x, ip 0x%x, write %d with ints disabled\n",
154 cr2, frame.eip, (frame.error_code & 0x2) != 0);
157 if(!kernel_startup) {
158 int_restore_interrupts(); // should enable the interrupts
159 adjust_int_disable_count = false;
160 ASSERT(int_are_interrupts_enabled());
164 ret = vm_page_fault(cr2, frame.eip,
165 (frame.error_code & 0x2) != 0,
166 (frame.error_code & 0x4) != 0,
167 &newip);
168 if(newip != 0) {
169 // the page fault handler wants us to modify the iframe to set the
170 // IP the cpu will return to to be this ip
171 frame.eip = newip;
173 break;
175 case SYSCALL_VECTOR: {
176 uint64 retcode;
177 unsigned int args[MAX_ARGS];
178 int rc;
181 thread_atkernel_entry();
183 #if 0
185 int i;
187 dprintf("i386_handle_trap: syscall %d, count %d, ptr 0x%x\n", frame.eax, frame.ecx, frame.edx);
188 dprintf(" call stack:\n");
189 for(i=0; i<frame.ecx; i++)
190 dprintf("\t0x%x\n", ((unsigned int *)frame.edx)[i]);
192 #endif
194 ** syscall interface works as such:
195 ** eax has syscall #
196 ** ecx has number of args (0-16)
197 ** edx has pointer to buffer containing args from first to last
198 ** each is verified to make sure someone doesn't try to clobber it
200 if(frame.ecx <= MAX_ARGS) {
201 if((addr_t)frame.edx >= KERNEL_BASE && (addr_t)frame.edx <= KERNEL_TOP) {
202 retcode = ERR_VM_BAD_USER_MEMORY;
203 } else {
204 rc = user_memcpy(args, (void *)frame.edx, frame.ecx * sizeof(unsigned int));
205 if(rc < 0)
206 retcode = ERR_VM_BAD_USER_MEMORY;
207 else
208 ret = syscall_dispatcher(frame.eax, (void *)args, &retcode);
210 } else {
211 // want to pass too many args into the system
212 retcode = ERR_INVALID_ARGS;
214 frame.eax = retcode & 0xffffffff;
215 frame.edx = retcode >> 32;
216 break;
218 default:
219 if(frame.vector >= 0x20) {
220 interrupt_ack(frame.vector); // ack the 8239 (if applicable)
221 ret = int_io_interrupt_handler(frame.vector);
222 } else {
223 panic("i386_handle_trap: unhandled cpu trap 0x%x at ip 0x%x!\n", frame.vector, frame.eip);
224 ret = INT_NO_RESCHEDULE;
226 break;
229 if(ret == INT_RESCHEDULE) {
230 int_disable_interrupts();
231 GRAB_THREAD_LOCK();
232 thread_resched();
233 RELEASE_THREAD_LOCK();
234 int_restore_interrupts();
237 if(frame.cs == USER_CODE_SEG || frame.vector == SYSCALL_VECTOR) {
238 thread_atkernel_exit();
240 // dprintf("0x%x cpu %d!\n", thread_get_current_thread_id(), smp_get_current_cpu());
242 if(t) {
243 i386_pop_iframe(t);
244 if(adjust_int_disable_count)
245 t->int_disable_level--; // keep the count in sync
249 int arch_int_init(kernel_args *ka)
251 idt = (desc_table *)ka->arch_args.vir_idt;
253 // setup the interrupt controller
254 out8(0x11, 0x20); // Start initialization sequence for #1.
255 out8(0x11, 0xa0); // ...and #2.
256 out8(0x20, 0x21); // Set start of interrupts for #1 (0x20).
257 out8(0x28, 0xa1); // Set start of interrupts for #2 (0x28).
258 out8(0x04, 0x21); // Set #1 to be the master.
259 out8(0x02, 0xa1); // Set #2 to be the slave.
260 out8(0x01, 0x21); // Set both to operate in 8086 mode.
261 out8(0x01, 0xa1);
262 out8(0xfb, 0x21); // Mask off all interrupts (except slave pic line).
263 out8(0xff, 0xa1); // Mask off interrupts on the slave.
265 set_intr_gate(0, &trap0);
266 set_intr_gate(1, &trap1);
267 set_intr_gate(2, &trap2);
268 set_intr_gate(3, &trap3);
269 set_intr_gate(4, &trap4);
270 set_intr_gate(5, &trap5);
271 set_intr_gate(6, &trap6);
272 set_intr_gate(7, &trap7);
273 // set_intr_gate(8, &trap8); /* handled below by pointing the idt entry to a tss segment */
274 set_intr_gate(9, &trap9);
275 set_intr_gate(10, &trap10);
276 set_intr_gate(11, &trap11);
277 set_intr_gate(12, &trap12);
278 set_intr_gate(13, &trap13);
279 set_intr_gate(14, &trap14);
280 // set_intr_gate(15, &trap15);
281 set_intr_gate(16, &trap16);
282 set_intr_gate(17, &trap17);
283 set_intr_gate(18, &trap18);
285 set_intr_gate(32, &trap32);
286 set_intr_gate(33, &trap33);
287 set_intr_gate(34, &trap34);
288 set_intr_gate(35, &trap35);
289 set_intr_gate(36, &trap36);
290 set_intr_gate(37, &trap37);
291 set_intr_gate(38, &trap38);
292 set_intr_gate(39, &trap39);
293 set_intr_gate(40, &trap40);
294 set_intr_gate(41, &trap41);
295 set_intr_gate(42, &trap42);
296 set_intr_gate(43, &trap43);
297 set_intr_gate(44, &trap44);
298 set_intr_gate(45, &trap45);
299 set_intr_gate(46, &trap46);
300 set_intr_gate(47, &trap47);
302 set_system_gate(SYSCALL_VECTOR, &trap99);
304 set_intr_gate(251, &trap251);
305 set_intr_gate(252, &trap252);
306 set_intr_gate(253, &trap253);
307 set_intr_gate(254, &trap254);
308 set_intr_gate(255, &trap255);
310 return 0;
313 int arch_int_init2(kernel_args *ka)
315 idt = (desc_table *)ka->arch_args.vir_idt;
316 vm_create_anonymous_region(vm_get_kernel_aspace_id(), "idt", (void *)&idt,
317 REGION_ADDR_EXACT_ADDRESS, PAGE_SIZE, REGION_WIRING_WIRED_ALREADY, LOCK_RW|LOCK_KERNEL);
319 return 0;