From e2a6ca9d03a3c0743384f0955609650f2cdce9bb Mon Sep 17 00:00:00 2001 From: Miguel Date: Thu, 18 Oct 2018 06:23:26 +0200 Subject: struggling with new syscalls --- kernel/interrupts.c | 105 +++++++++++++++++++++++++++++++++++++++++++--------- kernel/kernel.c | 2 +- kernel/kernel.h | 7 +++- kernel/log.c | 4 +- kernel/ringbuffer.c | 37 +++++++++++------- kernel/ringbuffer.h | 62 +++++++++++++++++++++---------- kernel/scheduler.c | 62 +++++++++++++++---------------- kernel/syscalls.c | 23 +++++++----- 8 files changed, 206 insertions(+), 96 deletions(-) (limited to 'kernel') diff --git a/kernel/interrupts.c b/kernel/interrupts.c index b7cdd74..8bc4f47 100644 --- a/kernel/interrupts.c +++ b/kernel/interrupts.c @@ -13,6 +13,7 @@ #include "apic.h" #include "ringbuffer.h" #include "compositor.h" +#include "syscalls.h" /** The size of our interrupts table */ #define INT_MAX 256 // 0-255 @@ -51,8 +52,37 @@ static void int_install_ir(int irq, uint16_t flags, uint16_t sel, void *addr) } /** - * Register an interrupt handler for given irq number. + * Register an interrupt handler for a given irq number. + * + * Some general notes on interrupts + * (TODO: consider moving this somewehre else) + * + * 1) In case of a syscall (coming from userspace) the handler should: + * a) For a blocking call, set required params and call the scheduler! + * b) Otherweise call x86_sti() by itself as soon as possiblle to + * reenable interrupts. + * + * 2) In case of APIC Timer interrupt call the scheduler. + * + * 3) Keyboard, Mouse, E1000, PIT + * just push your stuff somewhere for later processing as fast as + * you can and reschedule to a kernel worker OR just iret. + * + * 4) IPI + * we use them to force rescheduling only now + * treated in the same way as a APIC Timer interrupt... + * TODO: NOT CALL THIS IPI!! since we send it only on same cpu now :P + * + * NOTE: apic_eoi() should be called in all cases except the syscalls, + * to signal the "end of interrupt" to the APIC. + * + * TODO: REMEMBER THAT WE WILL HAVE TO CHECK ALL OUR SYSCALL ARE REENTRANT! + * ALSO IN MULTICPU SETTINGS! (check the userspace wrappers as well!) + * EACH prog requires a sufficient kernel-stack as well! (check this!) + * WE CAN GUARD with spinlocks / disabling interrupts when really needed.... + * */ + void interrupt_register(uint32_t irq, uint32_t func_addr) { if(irq<128||irq>160)kpanic("irq number out of range!"); @@ -72,36 +102,75 @@ uint32_t interrupt_handler(uint32_t esp, uint32_t irq) { uint32_t (*f)(uint32_t esp)=handlers[irq]; esp=f(esp); - apic_eoi(); + if(irq!=INTERRUPT_SYSCALL)apic_eoi(); return esp; } - if(irq==INTERRUPT_APIC_TIMER) + if(irq==INTERRUPT_APIC_TIMER || irq==INTERRUPT_IPI) { - if(cpu==0) - { - compositor_wake2(); - scheduler_wake_all(); - scheduler_wake_worker(esp); + if(cpu==0) // thi + { + // limit compositor to APIC_TIMER freq.(60hz?) + if(irq==INTERRUPT_APIC_TIMER)compositor_wake2(); } - } - if(irq==INTERRUPT_APIC_TIMER || irq==INTERRUPT_IPI) - { - esp=scheduler_run(esp,-1); + esp=scheduler_run(esp,-1); // just schedule to next task apic_eoi(); return esp; } if(irq==INTERRUPT_SYSCALL) { - apic_eoi(); + int pid=task_get_current_pid(); + uint32_t *stack; - stack=esp; - task_syscall(stack[11],stack[8],stack[10],stack[9]); //eax,ebx,ecx,edx - scheduler_wake_worker(esp); - esp=scheduler_run(esp,-1); - return esp; + stack=esp; + + // extract variables from stack + // (NOTE we pass them in some fucked up order, + // they were pushed via pusha! + uint32_t eax=stack[11]; + uint32_t ebx=stack[8]; + uint32_t ecx=stack[10]; + uint32_t edx=stack[9]; + + // only chance to do somehting before we reenable interrupts! + syscall_generic_prep(eax,edx,ecx,ebx,pid); + + // now we don't give a shit about gettting interrupted, yeah! + // this is guaranteed to cause heavy troubles... since we are + // not reentrant and do not guard anythin.. + + // keep brute force rescheduling till' we get through.... + // TODO: implement some waiting queue and wake only if there + // is any chance this will not fail again and again and again.. + // TODO: get rid of this big KERNEL LOCK + // it will also not work with SMP + while(true) + { + // x86_cli(); + int ok=syscall_generic_test(eax,edx,ecx,ebx,pid); + // x86_sti(); + if(ok)break; + else __asm__("int $0x81"); + } + + // uff, once we got through we can do the syscall and get out + // of this place... + // x86_cli(); + uint32_t ret=syscall_generic(eax,edx,ecx,ebx,pid); + // x86_sti(); + + stack[12]=0x1; // indicate with 0x1 we WANT to set a return + // value in ebx (0x0 will skip it) + stack[13]=ret; // and THIS is our return value! + + #ifdef LOG_SYSCALLS + klog("syscall ret=0x%08X",ret); + #endif + + //__asm__("int $0x81"); + return esp; // return to asm interrupt handler... and iret. } kpanic("unhandled interrupt %d",irq); diff --git a/kernel/kernel.c b/kernel/kernel.c index 31f169b..a630f94 100644 --- a/kernel/kernel.c +++ b/kernel/kernel.c @@ -175,7 +175,7 @@ void kernel_main(uint32_t eax,uint32_t ebx) klog("Unix Time = %u seconds",unixtime); klog("Symmetric Multi Processing (SMP) start ... "); - for(int i=1;iback+1)%f->size==f->front); + return((f->tail+1)%f->size==f->head); +} + +bool ringbuffer_not_full(ringbuffer* f) +{ + return((f->tail+1)%f->size!=f->head); } bool ringbuffer_empty(ringbuffer* f) { - return(f->front==f->back); + return(f->head==f->tail); } bool ringbuffer_has(ringbuffer* f) { - return !ringbuffer_empty(f); + return(f->head!=f->tail); } bool ringbuffer_put(ringbuffer* f,uint8_t c) { if(ringbuffer_full(f)) { +#ifdef RINGBUFFER_WARN klog("ringbuffer is full!"); +#endif return false; } - f->data[f->back]=c; - f->back++; - f->back%=f->size; - + f->data[f->tail]=c; + f->tail=(f->tail+1)%f->size; return true; } uint8_t ringbuffer_get(ringbuffer* f) { - if(ringbuffer_empty(f))return 0; // indistinguishable from value 0 :( // TODO + if(ringbuffer_empty(f)) + { +#ifdef RINGBUFFER_WARN + klog("ringbuffer is empty!"); +#endif + return 0; // indistinguishable from byte value 0x00. + } - uint8_t c = f->data[f->front]; - f->front++; - f->front%=f->size; + uint8_t c = f->data[f->head]; + f->head=(f->head+1)%f->size; return c; } diff --git a/kernel/ringbuffer.h b/kernel/ringbuffer.h index f68f766..9610150 100644 --- a/kernel/ringbuffer.h +++ b/kernel/ringbuffer.h @@ -8,16 +8,37 @@ * * Requires * -------- + * * Requires kballoc/kbfree - block allocation * - * Thread - * ------ - * This is __not__ threadsafe. It is your job to lock accesses to - * reads/writes. + * Thread Safety + * ------------- + * + * **NOTE** : If you have more than one producer and one consumer + * you will have to do some locking! + * + * ringbuffer_init() and ringbuffer_free() can be logically called only + * once anyway (for each ringbuffer). + * + * The structure is believed to be thread-safe as long as you have + * ONLY _ONE_ single producer AND _ONE_ single consumer PER ringbuffer. + * + * With one Consumer and one Producer: + * + * The Consumer can use: + * - ringbuffer_has() - will not give false positives. + * - ringbuffer_empty() - will not give false negatives. + * - ringbuffer_get() - acceseses only the head-pointer. + * + * The Producer can use: + * - ringbuffer_not_full() - will not give false positives + * - ringbuffer_full() - will not give false negatives. + * - ringbuffer_put() - accesses only the tail-pointer. * * Todo * ---- - * provide soemthing to read large blocks faster? + * provide soemthing to read large blocks faster. + * */ #ifndef RINGBUFFER_H @@ -27,12 +48,12 @@ #include /** Ringbuffer sturcutre */ -typedef struct ringbuffer_struct +typedef struct ringbuffer_struct { - uint32_t size; - uint32_t front; - uint32_t back; - uint8_t *data; + uint32_t size; // size in bytes + uint32_t head; // current head idx + uint32_t tail; // current tail idx + uint8_t *data; // the data itself }ringbuffer; /** Create a new fifo/ringbuffer of given size (in blocks) */ @@ -41,19 +62,22 @@ ringbuffer ringbuffer_init(uint32_t blocks); /** Deallocate buffer */ void ringbuffer_free(ringbuffer *f); -/** Put one _char_ into buffer. Returns true on success (i.e. buffer not full) */ -bool ringbuffer_put(ringbuffer*,uint8_t); +/** Put one _byte_ into the buffer. */ +bool ringbuffer_put(ringbuffer*,uint8_t); -/** Get a single _char_ from the buffer, - * Return value __0__ might indicate that the buffer is empty. - * check with _ringbuffer_has()_ to be sure. - */ +/** Get a single _byte_ from the buffer. */ uint8_t ringbuffer_get(ringbuffer*); -/** Check if buffer is not empty */ -bool ringbuffer_has(ringbuffer*); +/** Check if the buffer is not empty */ +bool ringbuffer_has(ringbuffer*); -/** Check if buffer is full */ +/** Check if the buffer is empty */ +bool ringbuffer_empty(ringbuffer*); + +/** Check if the buffer is full */ bool ringbuffer_full(ringbuffer* f); +/** Check if the buffer is not full */ +bool ringbuffer_not_full(ringbuffer* f); + #endif diff --git a/kernel/scheduler.c b/kernel/scheduler.c index 5a65b7e..8cadcb8 100644 --- a/kernel/scheduler.c +++ b/kernel/scheduler.c @@ -133,7 +133,7 @@ static uint32_t scheduler_schedule(uint32_t idx) current_task[cpu]=idx; - //klog("idx %d",idx); +// klog("cpu %d rescheduled to %d",cpu,idx); //klog("name: %s",task_list[cpu][idx].name); //klog("cpu %d / idx %d / pid %d / name: %5s",cpu,idx,task_list[cpu][idx].pid,task_list[cpu][idx].name); @@ -171,10 +171,16 @@ volatile uint32_t scheduler_run(uint32_t oldesp,uint32_t preference) } else task_list[cpu][current_task[cpu]].esp=oldesp; + if(preference>0) // try preference first if any.. + { + uint32_t esp=scheduler_schedule(preference); + if(esp)return esp; + } + for(int i=0;i