From fde77afb37de0aee545dcd21b90db681cb00d784 Mon Sep 17 00:00:00 2001 From: notion Date: Wed, 24 Jul 2002 20:59:25 +0000 Subject: [PATCH] Ported my NewOS changes to OpenBeOS. A couple of changes in various interrupt and thread functions and structures. These make it now possible to change the stack at any time without making the kernel crash. This is needed for calling VESA 3.0 VBE functions through the protected mode interface. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@422 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/arch/x86/arch_cpu.h | 9 +- headers/private/kernel/arch/x86/arch_stage2.h | 17 +++- headers/private/kernel/arch/x86/descriptors.h | 14 +++ headers/private/kernel/arch/x86/selector.h | 34 ++++++++ headers/private/kernel/arch/x86/stage2_priv.h | 10 --- .../private/kernel/arch/x86/thread_struct.h | 12 ++- src/kernel/core/arch/x86/Jamfile | 23 ++--- src/kernel/core/arch/x86/arch_cpu.c | 3 + src/kernel/core/arch/x86/arch_interrupts.S | 78 +++++++++++++++-- src/kernel/core/arch/x86/arch_selector.c | 86 +++++++++++++++++++ src/kernel/core/arch/x86/arch_thread.c | 28 +++--- src/kernel/core/arch/x86/arch_x86.S | 20 +++-- 12 files changed, 277 insertions(+), 57 deletions(-) create mode 100644 headers/private/kernel/arch/x86/descriptors.h create mode 100644 headers/private/kernel/arch/x86/selector.h create mode 100644 src/kernel/core/arch/x86/arch_selector.c 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