hardware multitasking problem

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
Brynet-Inc
Member
Member
Posts: 2426
Joined: Tue Oct 17, 2006 9:29 pm
Libera.chat IRC: brynet
Location: Canada
Contact:

Post by Brynet-Inc »

I haven't tested this... But it might work..

Code: Select all

void far_jmp(unsigned int selector) {
	__asm__ __volatile__ ("ljmp *%0": :"m" ((selector)) : "memory");
}
You might be able to use a define as well?

Code: Select all

#define far_jmp(selector) __asm__ __volatile__ ("ljmp *%0": :"m" ((selector)) : "memory");
In any sense.. The above code might not be correct..
Image
Twitter: @canadianbryan. Award by smcerm, I stole it. Original was larger.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Brendan wrote:Hi,

Just a quick note...
pcmattman wrote:

Code: Select all

void far_jmp(unsigned int selector) 
{ 
  asm ("ljmp %0": :"m" (selector)); 
} 
This won't work, because the selector for a far jump comes after the offset (not before). You'd actually need something like:

Code: Select all

    jmp far [address_of_selector_on_stack - 4]
I'm not sure about inline GAS syntax - something like this might work:

Code: Select all

void far_jmp(unsigned int selector) 
{ 
  asm ("ljmp -4(%0)": :"m" (selector)); 
} 

Cheers,

Brendan
Hi,

Every tutorial I've read says to use the direct offset. One thing that does need to be done is this:

Code: Select all

asm( "ljmp %0,$0" : : "m" (selector ) );
A far jump syntax is (not back-to-front like everything else in AT&T syntax)

Code: Select all

ljmp [seg],[offset]
Without the offset, GCC will not be able to assemble it.

Note: if the code I supplied doesn't work, try Brendan's idea, but keep the ',$0'.
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

As usual I got general protection fault I used every code you sent
but some of them gave me general protection fault and the rest of them
have a syntax problem and gcc gave me an error,so...
I'll write the steps of my hardware multitasking code,please correct me
if I'm incorrect:
*my multitasking consists of two tasks: main() and task(),task() just
prints a simple message.
*I built two more descriptors in my GDT one TSS descriptor for main()
and the other for task().
*the access value of this two descriptor was 0x89
*I built TSS struct with long 104 bytes,and I made two TSS one for each task.
*I write a init_task() function witch gives a primitive values to the tss,
if you would like you may take a look at it in a previous post.
*I loaded the TR with the selector of main().
the general protection fault happens when doing the long jump from
main() to the selector of task().


Thanx.
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

Ok I looked over the code again and did some debugging. Somehow the values in the TSSs are being stored in the wrong addresses. I think that somehow the compiler is aligning the structure or something like that. You are placing all of the correct values into the TSS but the values are going into the wrong locations in memory. This may not solve the problem but I believe that it will help, try changing the TSS declaration to the following, just make sure that you clear all of the new fields:

Code: Select all

struct __attribute__ ((__packed__)) tss_t
{

	word	link, __blh;
	dword	esp0;
	word	ss0, __ss0h;
	dword	esp1;
	word	ss1, __ss1h;
	dword	esp2;
	word	ss2, __ss2h;
	dword	cr3;
	dword	eip;
	dword	eflags;
	dword	eax, ecx, edx, ebx;
	dword	esp, ebp, esi, edi;
	word	es, __esh;
	word	cs, __csh;
	word	ss, __ssh;
	word	ds, __dsh;
	word	fs, __fsh;
	word	gs, __gsh;
	word	ldt, __ldth;
	word	trace, bitmap;

};
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post by Brendan »

Hi,

OK, I used AS and NDISASM, and NASM and OBJDUMP to work out exactly what AT&T syntax means what. The following should be 100% correct.
pcmattman wrote:A far jump syntax is (not back-to-front like everything else in AT&T syntax)

Code: Select all

ljmp [seg],[offset]
That is an "immediate far jump", not an "indirect far jump".

For an "immediate far jump" (where the target is hardcoded in the instruction) you'd want (NASM):

Code: Select all

    jmp far seg:offset
Or (AT&T):

Code: Select all

    jmp $seg,$offset
For an "indirect far jump" (where the target is stored in memory somewhere), you'd want (NASM):

Code: Select all

    jmp far [address_of_offset]
    jmp far [eax]
Or (AT&T):

Code: Select all

    ljmp *address_of_offset
    ljmp *(%eax)
For an "indirect far jump" (where the target is stored in memory somewhere, but you've got the address of the selector instead of the address of the offset (for 32-bit code), you'd want (NASM):

Code: Select all

    jmp far [address_of_selector - 4]
    jmp far [eax - 4]
Or (AT&T):

Code: Select all

    ljmp *address_of_selector-4
    ljmp *-4(%eax)
Combining all of this, when you want an indirect far jump in AT&T inline assembly where the characters "%0" are replaced by the (32-bit) address of where the selector is stored (and not the 32-bit address of where the offset is stored), you'd want:

Code: Select all

    __asm__ __volatile__ ("ljmp *-4%0\n" : :"m"(selector));
Unfortunately this doesn't work. GCC replaces the "%0" with "8(%ebp)" so you end up with:

Code: Select all

    ljmp *-48(%ebp)-4
It seems GAS/GCC is too stupid to add 2 numbers together and come up with the correct code:

Code: Select all

    ljmp *4(%ebp)
To fix this you need to get the compiler to calculate the address you want, rather than using GAS:

Code: Select all

void far_jmp(unsigned int volatile selector) {
    __asm__ __volatile__ ("ljmp *%0\n" : :"r"((void *)&selector - 4));
}
The final result (compiled with "-S" and no optimisation) is:

Code: Select all

.globl far_jmp
        .type   far_jmp, @function
far_jmp:
        pushl   %ebp
        movl    %esp, %ebp
        leal    4(%ebp), %eax
#APP
        ljmp *%eax

#NO_APP
        popl    %ebp
        ret
        .size   far_jmp, .-far_jmp
Compiled with "-S -fomit-frame-pointer -O3" you get:

Code: Select all

.globl far_jmp
        .type   far_jmp, @function
far_jmp:
#APP
        ljmp *%esp

#NO_APP
        ret
NOTE: If you don't define the function as "void far_jmp(unsigned int volatile selector)" and use "void far_jmp(unsigned int selector)" instead, then GCC's optimiser decides the value passed in "selector" doesn't get used and doesn't bother passing it - instead it does a far jump to an address stored in uninitialised data on the stack(!)...

[EDIT]
Hehe - found the problem. Use this instead:

Code: Select all

void far_jmp(unsigned int selector) {
    __asm__ __volatile__ ("ljmp *%0\n" : :"r"((void *)&selector - 4) : "memory");
}
It seems the clobberlist is useful for more than just clobbered things... ;)
[/EDIT]


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post by Brendan »

Hi,

I ran your last floppy image in Bochs.

It does "jmp far ss:[ebp+0x8]" but the memory at "ebp+0x8" contains:

Code: Select all

0x00104fd0:    0x00000020      0x00000100      0x00104fe8      0x001004de
This means the CPU will try to do a far jump to 0x00000100:0x00000020 (where 0x00000100 is the selector and 0x00000020 is the offset). The GDT doesn't contain anything for 0x00000100 (descriptor number 32), which is what causes the GPF.

The GDT does contain a 32-bit TSS descriptor at 0x00000020 (descriptor number 4). If the far jump was "jmp far ss:[ebp+0x4]" instead of "jmp far ss:[ebp+0x8]" then the CPU would have tried to do a far jump to 0x00000020:0x???????? (where 0x???????? is ignored because the descriptor is a valid task gate).

I put a breakpoint at the "jmp far ss:[ebp+0x8]" instruction, and changed EBP so that EBP = EBP - 4. After this it did the task switch without any exception.

Unfortunately it still didn't work though - after the task switch it started running code at 0x0008:0x0000198d, which contained nothing but zeros. The CS near the end of the TSS must have been correct though...


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

Hi...
First of all thank you Brendan so much for your hard work,
The CS near the end of the TSS must have been correct though...
I really didn't get it,what should i do to correct that ?

Unfortunately this code

Code: Select all

void far_jmp(unsigned int selector) 
{ 
   __asm__ __volatile__ ("ljmp *%0\n" : :"r"((void *)&selector -4)  : "memory");
}
wasn't acceptable by GCC it gave me an error.
Now I'm so confused I don't konw what to do and how I can fix my code :( :( :( :( :( :(


Thank you again Brendan,and all of you guys ...Thanx.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post by Brendan »

Hi,
abuashraf wrote:
The CS near the end of the TSS must have been correct though...
I really didn't get it,what should i do to correct that ?
If CS is already correct and it executes zeros after the task switch, then the value in the TSS's EIP field is wrong or something else is wrong (linker script, code that relocates the new task, etc)...

What actually happens after the task switch is that it executes code below 1 MB somewhere that's full of zeros (which the CPU thinks is ADD instructions) until it reaches something that isn't zero and crashes (possibly the EBDA or video display memory if you cleared the usable RAM, or perhaps some trash left by GRUB if you didn't).
abuashraf wrote:Unfortunately this code

Code: Select all

void far_jmp(unsigned int selector) 
{ 
   __asm__ __volatile__ ("ljmp *%0\n" : :"r"((void *)&selector -4)  : "memory");
}
wasn't acceptable by GCC it gave me an error.
Doh - just when I thought I'd got it right!

The correct code is:

Code: Select all

void far_jmp(unsigned int selector) 
{ 
   __asm__ __volatile__ ("ljmp *(%0)\n" : :"r"((void *)&selector -4)  : "memory");
}
I forgot the brackets around the %0 and was looking at the assembly generated by the compiler, which looked right to me.

Without the brackets the assembler expects a number and not a register and complains. For GAS each thing has to be in a specific place with the right syntax and various hand waving magic chants - it's not like NASM where anything that can be converted to valid machine language will work (for e.g. for "mov ebx,[gs:eax*3+foo]" in NASM you'd need something like "gs: movl *foo(%eax, 2, %eax),%ebx", which looks nothing like what it actually does).

Anyway, give that a try... :)


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
axelo
Posts: 1
Joined: Wed May 09, 2007 2:37 pm
Location: Sweden

Post by axelo »

Hi

In nasm I write

Code: Select all

jmp base:offset
to make a far jump and to reload cs (e.g. load new gdt)

Often base is hardcoded to 0x08, but I was wondering if it is possible to let the base be in a memory location or in a reg. I tried some combinations but nasm complains of invalid combination.

Not working:

Code: Select all

jmp ax:.flush

Code: Select all

jmp word [sel]:.flush
I looked in helppc, jmp but I don't fully understand all ptr and 32 thingys.

Maybe Brendan already has answered this, although, please enlighten me :)
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

Hi...
Finally it's working ....unbelievable :D :D
you were amazing Brendan thank you,thank you frank so much...
and all of you guys you've been so helpfull.
I already implemented paging in my kernel but when I was working
on multitasking I disabled paging so not to get page fault exception.
Now before my multitasking gets more complicated,(till now it's simple)
I want to enable paging with multitasking how can I do that?
About my code I used Brendan's far_jmp() I mean this:

Code: Select all

void far_jmp(unsigned int selector) 
{ 
   __asm__ __volatile__ ("ljmp *(%0)\n" : :"r"((void *)&selector -4)  : "memory"); 
}
also as frank said I already casted the "word" to "dword" in Tss descriptors
and I did the same in my function "init_task()".

Note:
axelo if you want some code I can send it to you also all the informations you need
Brendan,frank and the rest of the guys already talk about...

Thanx.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post by Brendan »

Hi,
axelo wrote:Often base is hardcoded to 0x08, but I was wondering if it is possible to let the base be in a memory location or in a reg. I tried some combinations but nasm complains of invalid combination.
80x86 CPUs don't support what you're looking for. To reload CS and EIP at the same time you'd use an indirect jump:

Code: Select all

    mov dword [some_address], the_offset
    mov word [some_address + 4], the_selector
    jmp [some_address]
The alternative is to use RETF instead:

Code: Select all

    push dword the_selector
    push dword the_offset
    retf
RETF works if the target is a code segment, but has different behaviour than a far jump if the target is a task gate (see the section on nested tasks in the manual if you need to know the difference).

Hard-coding the target is easier and faster if you can use it... ;)


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post by Brendan »

Hi,
abuashraf wrote:Finally it's working ....unbelievable :D :D
you were amazing Brendan thank you,thank you frank so much...
Congratulations :)

I would've been more amazing if I didn't give wrong information twice - glad I could help, but also glad I can forget about AT&T syntax again.. ;)
abuashraf wrote:I already implemented paging in my kernel but when I was working
on multitasking I disabled paging so not to get page fault exception.
Now before my multitasking gets more complicated,(till now it's simple)
I want to enable paging with multitasking how can I do that?
It's mostly the same as it was before you started adding multitasking - the main difference is that you need to put something in each TSS's CR3 field. For now, just use the same page directory for all tasks (but sooner or later you'll probably want to create new page directories for new tasks so they run in their own address space).


Cheers,

Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Post Reply