diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h index 37bc66a854..737b2c7f86 100644 --- a/headers/private/kernel/arch/x86/arch_cpu.h +++ b/headers/private/kernel/arch/x86/arch_cpu.h @@ -7,12 +7,9 @@ /* ??? why include this as we're normally included from that file! */ #include +#include +#include -#define KERNEL_CODE_SEG 0x8 -#define KERNEL_DATA_SEG 0x10 -#define USER_CODE_SEG 0x1b -#define USER_DATA_SEG 0x23 -#define TSS 0x28 #define PAGE_SIZE 4096 #define _BIG_ENDIAN 0 @@ -88,7 +85,7 @@ typedef struct pdentry { #define nop() __asm__ ("nop"::) void setup_system_time(unsigned int cv_factor); -void i386_context_switch(unsigned int **old_esp, unsigned int *new_esp, addr new_pgdir); +void i386_context_switch(struct arch_thread *old_state, struct arch_thread *new_state, addr new_pgdir); void i386_enter_uspace(addr entry, void *args, addr ustack_top); void i386_set_kstack(addr kstack); void i386_switch_stack_and_call(addr stack, void (*func)(void *), void *arg); diff --git a/headers/private/kernel/arch/x86/arch_stage2.h b/headers/private/kernel/arch/x86/arch_stage2.h index a4318fb148..8bbf73d84e 100644 --- a/headers/private/kernel/arch/x86/arch_stage2.h +++ b/headers/private/kernel/arch/x86/arch_stage2.h @@ -1,15 +1,26 @@ -/* +/* ** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. ** Distributed under the terms of the NewOS License. */ -#ifndef _NEWOS_KERNEL_BOOT_ARCH_I386_STAGE2_H -#define _NEWOS_KERNEL_BOOT_ARCH_I386_STAGE2_H +#ifndef _KERNEL_ARCH_x86_STAGE2_H +#define _KERNEL_ARCH_x86_STAGE2_H #include #define MAX_BOOT_PTABLES 4 +#define _PACKED __attribute__((packed)) + +#define IDT_LIMIT 0x800 +#define GDT_LIMIT 0x800 + +struct gdt_idt_descr { + unsigned short a; + unsigned int *b; +} _PACKED; + + // kernel args typedef struct { // architecture specific diff --git a/headers/private/kernel/arch/x86/descriptors.h b/headers/private/kernel/arch/x86/descriptors.h new file mode 100644 index 0000000000..01ec50e469 --- /dev/null +++ b/headers/private/kernel/arch/x86/descriptors.h @@ -0,0 +1,14 @@ +/* +** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +#ifndef _KERNEL_ARCH_x86_DESCRIPTORS_H +#define _KERNEL_ARCH_x86_DESCRIPTORS_H + +#define KERNEL_CODE_SEG 0x8 +#define KERNEL_DATA_SEG 0x10 +#define USER_CODE_SEG 0x1b +#define USER_DATA_SEG 0x23 +#define TSS 0x28 + +#endif \ No newline at end of file diff --git a/headers/private/kernel/arch/x86/selector.h b/headers/private/kernel/arch/x86/selector.h new file mode 100644 index 0000000000..a17e85340d --- /dev/null +++ b/headers/private/kernel/arch/x86/selector.h @@ -0,0 +1,34 @@ +/* +** Copyright 2002, Michael Noisternig. All rights reserved. +** Distributed under the terms of the NewOS License. +*/ +#ifndef _KERNEL_ARCH_x86_SELECTOR_H +#define _KERNEL_ARCH_x86_SELECTOR_H + +#include + +typedef uint32 selector_id; +typedef uint64 selector_type; + +// DATA segments are read-only +// CODE segments are execute-only +// both can be modified by using the suffixed enum versions +// legend: w = writable +// d = expand down +// r = readable +// c = conforming +enum segment_type { + DATA = 0x8, DATA_w, DATA_d, DATA_wd, CODE, CODE_r, CODE_c, CODE_rc +}; + +#define SELECTOR(base,limit,type,mode32) \ + (((uint64)(((((uint32)base)>>16)&0xff) | (((uint32)base)&0xff000000) | ((type)<<9) | ((mode32)<<22) | (1<<15))<<32) \ + | ( (limit) >= (1<<20) ? (((limit)>>12)&0xffff) | ((uint64)(((limit)>>12)&0xf0000)<<32) | ((uint64)1<<(23+32)) : ((limit)&0xffff) | ((uint64)((limit)&0xf0000)<<32) ) \ + | ((((uint32)base)&0xffff)<<16)) + +void i386_selector_init( void *gdt ); +selector_id i386_selector_add( selector_type selector ); +void i386_selector_remove( selector_id id ); +selector_type i386_selector_get( selector_id id ); + +#endif diff --git a/headers/private/kernel/arch/x86/stage2_priv.h b/headers/private/kernel/arch/x86/stage2_priv.h index f4f2c6586f..b27030ccef 100755 --- a/headers/private/kernel/arch/x86/stage2_priv.h +++ b/headers/private/kernel/arch/x86/stage2_priv.h @@ -39,16 +39,6 @@ void cpuid(unsigned int selector, unsigned int *data); #define SCREEN_HEIGHT 24 #define ADDR_MASK 0xfffff000 -#define _PACKED __attribute__((packed)) - -#define IDT_LIMIT 0x800 -#define GDT_LIMIT 0x800 - -struct gdt_idt_descr { - unsigned short a; - unsigned int *b; -} _PACKED; - // SMP stuff extern int smp_boot(kernel_args *ka, unsigned int kernel_entry); extern void smp_trampoline(void); diff --git a/headers/private/kernel/arch/x86/thread_struct.h b/headers/private/kernel/arch/x86/thread_struct.h index 508be3d54b..0dd2856d84 100755 --- a/headers/private/kernel/arch/x86/thread_struct.h +++ b/headers/private/kernel/arch/x86/thread_struct.h @@ -2,12 +2,18 @@ ** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. ** Distributed under the terms of the NewOS License. */ -#ifndef _NEWOS_KERNEL_ARCH_I386_THREAD_STRUCT_H -#define _NEWOS_KERNEL_ARCH_I386_THREAD_STRUCT_H +#ifndef _KERNEL_ARCH_x86_THREAD_STRUCT_H +#define _KERNEL_ARCH_x86_THREAD_STRUCT_H + +struct farcall { + unsigned int *esp; + unsigned int *ss; +}; // architecture specific thread info struct arch_thread { - unsigned int *esp; + struct farcall current_stack; + struct farcall interrupt_stack; // 512 byte floating point save point uint8 fpu_state[512]; }; diff --git a/src/kernel/core/arch/x86/Jamfile b/src/kernel/core/arch/x86/Jamfile index a36db98c2e..407c41285a 100644 --- a/src/kernel/core/arch/x86/Jamfile +++ b/src/kernel/core/arch/x86/Jamfile @@ -1,16 +1,17 @@ SubDir OBOS_TOP src kernel core arch x86 ; -KernelStaticLibrary libx86 : - <$(SOURCE_GRIST)>arch_cpu.c - <$(SOURCE_GRIST)>arch_dbg_console.c - <$(SOURCE_GRIST)>arch_debug.c - <$(SOURCE_GRIST)>arch_faults.c - <$(SOURCE_GRIST)>arch_int.c - <$(SOURCE_GRIST)>arch_smp.c - <$(SOURCE_GRIST)>arch_thread.c - <$(SOURCE_GRIST)>arch_timer.c - <$(SOURCE_GRIST)>arch_vm.c - <$(SOURCE_GRIST)>arch_vm_translation_map.c +KernelStaticLibrary libx86 : + <$(SOURCE_GRIST)>arch_cpu.c + <$(SOURCE_GRIST)>arch_dbg_console.c + <$(SOURCE_GRIST)>arch_debug.c + <$(SOURCE_GRIST)>arch_faults.c + <$(SOURCE_GRIST)>arch_int.c + <$(SOURCE_GRIST)>arch_selector.c + <$(SOURCE_GRIST)>arch_smp.c + <$(SOURCE_GRIST)>arch_thread.c + <$(SOURCE_GRIST)>arch_timer.c + <$(SOURCE_GRIST)>arch_vm.c + <$(SOURCE_GRIST)>arch_vm_translation_map.c <$(SOURCE_GRIST)>arch_x86.S <$(SOURCE_GRIST)>arch_interrupts.S : diff --git a/src/kernel/core/arch/x86/arch_cpu.c b/src/kernel/core/arch/x86/arch_cpu.c index 94b25ddf0e..91b614692c 100755 --- a/src/kernel/core/arch/x86/arch_cpu.c +++ b/src/kernel/core/arch/x86/arch_cpu.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -52,6 +53,8 @@ arch_cpu_init2(kernel_args *ka) vm_create_anonymous_region(vm_get_kernel_aspace_id(), "gdt", (void **)&gdt, REGION_ADDR_EXACT_ADDRESS, PAGE_SIZE, REGION_WIRING_WIRED_ALREADY, LOCK_RW|LOCK_KERNEL); + i386_selector_init( gdt ); // pass the new gdt + tss = kmalloc(sizeof(struct tss *) * ka->num_cpus); if (tss == NULL) { panic("arch_cpu_init2: could not allocate buffer for tss pointers\n"); diff --git a/src/kernel/core/arch/x86/arch_interrupts.S b/src/kernel/core/arch/x86/arch_interrupts.S index 5cbe6da4d8..37c268c599 100755 --- a/src/kernel/core/arch/x86/arch_interrupts.S +++ b/src/kernel/core/arch/x86/arch_interrupts.S @@ -1,7 +1,13 @@ -/* +/* ** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Copyright 2002, Michael Noisternig. All rights reserved. ** Distributed under the terms of the NewOS License. */ + +#include + +#define FUNCTION(x) .global x; .type x,@function; x + .text #define TRAP_ERRC(name, vector) \ @@ -10,7 +16,7 @@ name: \ pushl $vector; \ jmp int_bottom - + #define TRAP(name, vector) \ .globl name; \ .align 8; \ @@ -64,7 +70,7 @@ TRAP(trap253, 253) TRAP(trap254, 254) TRAP(trap255, 255) -.align 8 +.align 16 .globl int_bottom int_bottom: pusha @@ -72,9 +78,14 @@ int_bottom: push %es push %fs push %gs - movw $0x10,%ax - movw %ax,%ds + movl $KERNEL_DATA_SEG,%eax cld + movl %eax,%ds + movl %ss,%ebx + movl %esp,%esi + cmpl %eax,%ebx // check if we changed the stack + jne custom_stack + kernel_stack: call i386_handle_trap pop %gs pop %fs @@ -83,3 +94,60 @@ int_bottom: popa addl $8,%esp iret + + // custom stack -> copy registers to kernel stack and switch there + custom_stack: + movl %dr3,%edx // get_current_thread + movl %eax,%es + addl _interrupt_stack_offset,%edx + lss (%edx),%esp + movl %ebx,%ds + subl $84,%esp + movl %esp,%edi + movl $19,%ecx + rep movsl + movl %eax,%ds + subl $76,%esi + movl %esi,(%edi) // save custom stack address + movl %ebx,4(%edi) + call i386_handle_trap + lss 76(%esp),%esp // reload custom stack address + pop %gs + pop %fs + pop %es + pop %ds + popa + addl $8,%esp + iret + +_interrupt_stack_offset: +.long 0 + +// void i386_stack_init(struct farcall *interrupt_stack_offset) + /* setup in arch_thread.c: arch_thread_init_thread_struct() */ +FUNCTION(i386_stack_init): + movl 4(%esp),%eax + movl %eax,_interrupt_stack_offset + ret + +// void i386_stack_switch(struct farcall new_stack) +FUNCTION(i386_stack_switch): + movl %dr3,%eax // get_current_thread + movl (%esp),%edx + pushf + popl %ecx + addl _interrupt_stack_offset,%eax + cli + pushl %ss + cmpl $KERNEL_DATA_SEG,(%esp) + je kernel_stack2 + popl %eax + jmp switch + kernel_stack2: + popl 4(%eax) + movl %esp,(%eax) + switch: + lss 4(%esp),%esp + pushl %ecx + popf + jmp *%edx diff --git a/src/kernel/core/arch/x86/arch_selector.c b/src/kernel/core/arch/x86/arch_selector.c new file mode 100644 index 0000000000..d47eea1673 --- /dev/null +++ b/src/kernel/core/arch/x86/arch_selector.c @@ -0,0 +1,86 @@ +/* +** Copyright 2002, Michael Noisternig. All rights reserved. +** Distributed under the terms of the NewOS License. +** Distributed under the terms of the MIT License as part of the OpenBeOS project. +*/ +#include +#include +#include +#include + +#include + +#define MAX_SELECTORS (GDT_LIMIT/8) +#define ENTRIES (MAX_SELECTORS/(sizeof(uint32)*8)) + +static uint32 selector_bitmap[ENTRIES] + = { 0x000000ff }; // first 8 selectors reserved + +static selector_type *gdt_table; +static struct gdt_idt_descr descr = { GDT_LIMIT - 1, NULL }; + +void i386_selector_init( void *gdt ) +{ + gdt_table = (selector_type *)gdt; + descr.b = (unsigned int *)gdt_table; +} + +// creates a new selector in the gdt of given type (use SELECTOR macro) +// IN: selector type +// RET: selector that can be directly used for segment registers +// 0 on error +selector_id i386_selector_add( selector_type type ) +{ + static int spinlock; + int state; + uint32 mask; + selector_id id = 0; + unsigned i; + + state = int_disable_interrupts(); + acquire_spinlock( &spinlock ); + + for ( i = 0; i < ENTRIES; i++ ) + if ( selector_bitmap[i] != 0xffffffff ) { // found free place in there + id = i*sizeof(uint32)*8; + mask = 1; + while ( selector_bitmap[i] & mask ) { + mask <<= 1; + id++; + } + selector_bitmap[i] |= mask; + gdt_table[id] = type; + break; + } + + release_spinlock( &spinlock ); + int_restore_interrupts( state ); + + if ( id ) { + asm("lgdt %0;" + : : "m" (descr)); + } + + return id*8; +} + +// removes a selector with given id from the gdt +void i386_selector_remove( selector_id id ) +{ + if ( id < 8*8 || id >= MAX_SELECTORS*8 ) + return; + + id /= 8; + gdt_table[id] = 0; + + atomic_and( &selector_bitmap[id/32], ~(1<<(id&31)) ); + + asm("lgdt %0;" + : : "m" (descr)); +} + +// returns the selector type of a given id +selector_type i386_selector_get( selector_id id ) +{ + return gdt_table[id/8]; +} diff --git a/src/kernel/core/arch/x86/arch_thread.c b/src/kernel/core/arch/x86/arch_thread.c index 154cd08151..1d32127c96 100755 --- a/src/kernel/core/arch/x86/arch_thread.c +++ b/src/kernel/core/arch/x86/arch_thread.c @@ -17,12 +17,17 @@ int arch_proc_init_proc_struct(struct proc *p, bool kernel) return 0; } +// from arch_interrupts.S +extern void i386_stack_init( struct farcall *interrupt_stack_offset ); + int arch_thread_init_thread_struct(struct thread *t) { - t->arch_info.esp = NULL; + // set up an initial state (stack & fpu) + memset(&t->arch_info, 0, sizeof(t->arch_info)); - // set up an initial fpu state - memset(&t->arch_info.fpu_state[0], 0, sizeof(t->arch_info.fpu_state)); + // let the asm function know the offset to the interrupt stack within struct thread + // I know no better ( = static) way to tell the asm function the offset + i386_stack_init( &((struct thread*)0)->arch_info.interrupt_stack ); return 0; } @@ -62,8 +67,9 @@ int arch_thread_initialize_kthread_stack(struct thread *t, int (*start_func)(voi *kstack_top = 0; } - // save the esp - t->arch_info.esp = kstack_top; + // save the stack position + t->arch_info.current_stack.esp = kstack_top; + t->arch_info.current_stack.ss = (int *)KERNEL_DATA_SEG; return 0; } @@ -79,10 +85,11 @@ void arch_thread_context_switch(struct thread *t_from, struct thread *t_to) #if 0 int i; - dprintf("arch_thread_context_switch: cpu %d 0x%x -> 0x%x, aspace 0x%x -> 0x%x, &old_esp = 0x%p, esp = 0x%p\n", + dprintf("arch_thread_context_switch: cpu %d 0x%x -> 0x%x, aspace 0x%x -> 0x%x, old stack = 0x%x:0x%x, stack = 0x%x:0x%x\n", smp_get_current_cpu(), t_from->id, t_to->id, t_from->proc->aspace, t_to->proc->aspace, - &t_from->arch_info.esp, t_to->arch_info.esp); + t_from->arch_info.current_stack.ss, t_from->arch_info.current_stack.esp, + t_to->arch_info.current_stack.ss, t_to->arch_info.current_stack.esp); #endif #if 0 for(i=0; i<11; i++) @@ -120,7 +127,7 @@ void arch_thread_context_switch(struct thread *t_from, struct thread *t_to) #if 0 { - int a = *(int *)(t_to->arch_info.esp - 4); + int a = *(int *)(t_to->arch_info.current_stack.esp - 4); } #endif @@ -128,14 +135,15 @@ void arch_thread_context_switch(struct thread *t_from, struct thread *t_to) panic("arch_thread_context_switch: bad pgdir 0x%lx\n", new_pgdir); i386_fsave_swap(t_from->arch_info.fpu_state, t_to->arch_info.fpu_state); - i386_context_switch(&t_from->arch_info.esp, t_to->arch_info.esp, new_pgdir); + i386_context_switch(&t_from->arch_info, &t_to->arch_info, new_pgdir); } void arch_thread_dump_info(void *info) { struct arch_thread *at = (struct arch_thread *)info; - dprintf("\tesp: %p\n", at->esp); + dprintf("\tesp: %p\n", at->current_stack.esp); + dprintf("\tss: %p\n", at->current_stack.ss); dprintf("\tfpu_state at %p\n", at->fpu_state); } diff --git a/src/kernel/core/arch/x86/arch_x86.S b/src/kernel/core/arch/x86/arch_x86.S index 454b2ef277..ff21c2e019 100755 --- a/src/kernel/core/arch/x86/arch_x86.S +++ b/src/kernel/core/arch/x86/arch_x86.S @@ -1,5 +1,6 @@ /* ** Copyright 2001, Travis Geiselbrecht. All rights reserved. +** Copyright 2002, Michael Noisternig. All rights reserved. ** Distributed under the terms of the NewOS License. */ @@ -75,13 +76,12 @@ FUNCTION(setup_system_time): movl %eax,cv_factor ret -/* long long system_time(); */ +/* uint64 system_time(); */ FUNCTION(system_time): /* load 64-bit factor into %eax (low), %edx (high) */ rdtsc /* time in %edx,%eax */ pushl %ebx - pushl %ecx movl cv_factor, %ebx movl %edx, %ecx /* save high half */ mull %ebx /* truncate %eax, but keep %edx */ @@ -92,7 +92,6 @@ FUNCTION(system_time): subl %ebx, %ebx /* need zero to propagate carry */ addl %ecx, %eax adc %ebx, %edx - popl %ecx popl %ebx ret @@ -118,18 +117,21 @@ FUNCTION(i386_fxsave_swap): fxrstor (%eax) ret -/* void i386_context_switch(unsigned int **old_esp, unsigned int *new_esp, addr new_pgdir); */ +/* void i386_context_switch(struct arch_thread *old_state, struct arch_thread *new_state, addr new_pgdir); */ FUNCTION(i386_context_switch): pusha /* pushes 8 words onto the stack */ - movl 36(%esp),%eax + movl 36(%esp),%eax /* save current_stack */ movl %esp,(%eax) + pushl %ss + popl %edx + movl %edx,4(%eax) movl 44(%esp),%eax /* get possible new pgdir */ - cmpl $0x0,%eax /* is it null? */ + orl %eax,%eax /* is it null? */ je skip_pgdir_swap - movl %eax,%cr3 + movl %eax,%cr3 skip_pgdir_swap: - movl 40(%esp),%eax - movl %eax,%esp + movl 40(%esp),%eax /* get new current_stack */ + lss (%eax),%esp popa ret