Page 1 of 1

Software Based Multitasking

Posted: Thu Dec 27, 2007 10:17 am
by t0xic
Hey everyone,

I have decided to go with software-based multitasking, and I have already written some code for it. I'm not sure when I should call the task switcher. Should I call it in my IRQ code, or in the actual 8253 timer handler? Here is my code:

Switch.asm:

Code: Select all

extern kernel_entry
global tswitch
tswitch:
	push gs
	push fs
	push es
	push ds
	push ebp
	push edi
	push esi
	push edx
	push ecx
	push ebx
	push eax
	
	mov ebx, esp
	mov esp, stacktop
	push ebx
	mov ax, 10h	
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	call kernel_entry
	pop ebx
	mov esp, ebx
	
	pop eax
	pop ebx
	pop ecx
	pop edx
	pop esi
	pop edi
	pop ebp
	pop ds
	pop es
	pop fs
	pop gs
	add esp, 8
	iret
		
[section .bss]	
	
resb 8192	
stacktop:
C switch:

Code: Select all

#include <regs.h>

int kernel_entry( struct regs *r )
{
	printf("Entry\n");
	//asm("hlt"); //If I have this here, no exception
	return r;
}

Code: Select all

void timer_handler(struct regs *r)
{
    timer_ticks++; 
	printf("Tick: %i\n", timer_ticks);
	if (!( timer_ticks % 100 ))
	{
		printf("switching...");
		tswitch();
		printf("returning...");
			
	}
}
Exception:

Code: Select all

00022236158e[CPU0 ] check_cs: not a valid code segment !
00076686000p[WGUI ] >>PANIC<< POWER button turned off.
00076686000i[SYS  ] Last time is 1198772194
00076686000i[CPU0 ] protected mode
00076686000i[CPU0 ] CS.d_b = 32 bit
00076686000i[CPU0 ] SS.d_b = 32 bit
00076686000i[CPU0 ] | EAX=000000f0  EBX=00067dd8  ECX=00000026  EDX=000003d5
00076686000i[CPU0 ] | ESP=00067dc4  EBP=00067dcc  ESI=0002beb5  EDI=0002beb6
00076686000i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af pf cf
00076686000i[CPU0 ] | SEG selector     base    limit G D
00076686000i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
00076686000i[CPU0 ] |  CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
00076686000i[CPU0 ] |  DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00076686000i[CPU0 ] |  SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00076686000i[CPU0 ] |  ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
00076686000i[CPU0 ] |  FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00076686000i[CPU0 ] |  GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
00076686000i[CPU0 ] | EIP=00100984 (00100984)
00076686000i[CPU0 ] | CR0=0x80000011 CR1=0 CR2=0x00000000
00076686000i[CPU0 ] | CR3=0x0010b000 CR4=0x00000000
00076686000i[CPU0 ] >> jmp .+0xfffffffe (0x00100984) : EBFE
00076686000i[     ] restoring default signal behavior
00076686000i[CTRL ] quit_sim called with exit code 1
Edit:
Just started getting this error:

Code: Select all

fetch_raw_descriptor: LDT: index (7e97)fd2 > limit (0)
Thanks in advance for your help

--Michael

Posted: Thu Dec 27, 2007 2:06 pm
by bewing
There are other IRQ0 generators than the 8253 (specifically: APICs) -- so I'd think you would definitely want to do it in the IRQ0 handler.

My IRQ0 handler deals with the hardware, updates the system clock, then checks the global "atomic" flag -- if that is set, it returns with an IRET rather than going to the scheduler, then it checks to see if it was the kernel that was interrupted (ring 0) -- the kernel is always run atomically (does an IRET again), last, it checks to see if the scheduler has been "turned on" yet -- if not, IRET -- if yes, then it jumps to the scheduler entrypoint, and the scheduler is likely to IRET to a new, different task.

Posted: Thu Dec 27, 2007 2:12 pm
by t0xic
How do you IRET to a task? Afaik, you just update ss0, esp0 and eip in the tss right? Then do an IRET? I'll try that.
Thanks,
Michael

Posted: Thu Dec 27, 2007 6:25 pm
by bewing
The exact IRET process from the scheduler's point of view depends on what ring the scheduler is running in, and which ring the next task to run is in.
Assuming ring0/ring3, then you need 2 stacks for each ring3 task. The task's "normal" local stack, and a special ring0 stack that gets loaded with the IRET info when the task is swapped out -- when IRQ0 fires. You point esp at the new task's ring0 stack before IRETting to the new task.

You may need to put more info than just IRET info on that ring0 stack, of course -- that's the easiest place to store the complete CPU state of the task when it gets swapped out. CR3, floating point registers, MMX, SSE .... You will certainly want to reload some of that before doing the final IRET to the new task.

Posted: Thu Dec 27, 2007 6:57 pm
by t0xic
Thanks for your reply bewing.

For now, I just want to stick with ring 0 tasks. I've re-written a majority of the code, and it is getting closer to working... just sorting out some gdt issues.

--Michael