OSDev.org

The Place to Start for Operating System Developers
It is currently Wed Apr 24, 2024 12:39 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: FPU stub
PostPosted: Sun Nov 09, 2008 10:58 pm 
Offline
Member
Member
User avatar

Joined: Fri Jun 22, 2007 12:47 pm
Posts: 1598
Location: New Hampshire, USA
I decided to start a page on the x87 FPU. I didn't see any documents on it in the wiki and I think it should at least have a stub (which is what I did, and I will get around to make it a little more robust later). It is an important part of the x86 arch and can provide extremely powerful functionality.

btw, out of curiousity due to the lack of wiki-info, how many of you guys actually implement FPU integers (float, double, long double)?

_________________
Website: https://Joscor.com


Top
 Profile  
 
 Post subject: Re: FPU stub
PostPosted: Mon Nov 10, 2008 12:59 pm 
Offline
Member
Member
User avatar

Joined: Thu Dec 21, 2006 7:42 pm
Posts: 1391
Location: Unknown. Momentum is pretty certain, however.
If I knew more about the FPU, I'd add some (and I'd research it if I had time).

In my opinion, the code you've given is good, but it needs more descriptions (I do know what the instructions mean, but if I was just learning about the FPU for the first time, I'd have some questions):
    I can see that you're setting a bit in CR4, ok.
    finit must initialize the FPU, alright.
    How did you get that mode? What does it mean? What other modes are there? What does fldcw mean, or whats that instruction do?

The code you gave doesn't compile on my computer. I get "Error: suffix or operands invalid for `movq'".
After changing it to 'mov' it works and executes without dying, but is this ok?

Good start, the wiki needed a page for the FPU.

-JL

_________________
SeaOS: Adding VT-x, networking, and ARM support
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io


Top
 Profile  
 
 Post subject: Re: FPU stub
PostPosted: Mon Nov 10, 2008 6:40 pm 
Offline
Member
Member
User avatar

Joined: Wed Oct 18, 2006 3:45 am
Posts: 9301
Location: On the balcony, where I can actually keep 1½m distance
My kernel has FPU support, including SSE and friends, and with lazy task switching. Basically my approach is to create an FPU image in the task state, drop in the constants needed for SSE to work, then just reload cr0 with the TS bit upon task switch.

Then when the FPU is actually needed, the previous state is stored and the new state loaded.

You are free to post these snippets on the wiki. Probably best if someone doublechecked them in case I misinterpreted something from the manuals - I don't have working 386s and 486s to check if all code paths do what they should.
Code:
DetectCoprocessor:      CPU 386 FPU
                        MOV EDX, CR0                            ; CR0 has a bit on the matter as well
                        AND EDX, CR0_ET                         ; Check Extension Type
                        JZ .nofpu                               ; The processor supports no FPU
                        MOV EDX, CR0                            ; Start probe, get CR0
                        AND EDX, (-1) - (CR0_TS + CR0_EM)       ; clear TS and EM to force fpu access
                        OR EDX, CR0_NE                          ; set NE (workaround no-wait bug)
                        MOV CR0, EDX                            ; store control word
                        FNINIT                                  ; load defaults to FPU
                        FNSTSW [.testword]                      ; store status word. If there's no coprocessor, nothing happens
                        CMP word [.testword], 0                 ; compare the written status
                        JNE .nofpu
.hasfpu:                ; do something when there is an FPU installed

.nofpu:                 ; do something when there isn't an FPU installed
                        RET
.testword:              DW 0x55AA

Useful for those machines when there's no CPUID to ask, or the coprocessor isn't on-chip

Anyway, if you have a CPU without on-chip FPU, you might want to do paranoia checks on the interrupt handling that it indeed works, otherwise, you should set CR0.NE since that doesn't require your chipset to have that wiring in place.

If you want to enable SSE, you should check the CPUID bits for SSE and Fast save-restore functionality. If your processor can handle that, you can set CR4.OSFXSR and CR4.OSXMMEXCPT.

To switch FPU states you'll have to store the context. Note that this differs when SSE is enabled. FNSAVE/FNRSTOR doesn't store SSE state, FXSAVE/FXRSTOR do but you'll get errors when you try that on a non-sse-enabled machine
Code:
                        ; EDX -> store data
                        ; EAX -> get data

                        CMP dword [sse_enabled], 0
                        JE .fpuonly

                        CPU 686
.fpuandsse:             CLTS
                        FXSAVE [EDX]
                        FXRSTOR [EAX]
                        JMP .done2

.fpuonly:               CLTS
                        FNSAVE [EDX]
                        FRSTOR [EAX]
                        JMP .done2
                        CPU 386


For FPU support, you should write the #NM handler (for handling lazy FPU switches) and #MF handler (for dealing with FPU exceptions)
For SSE, you should write the XF handler as well (for SIMD exceptions).

_________________
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]


Top
 Profile  
 
 Post subject: Re: FPU stub
PostPosted: Mon Nov 10, 2008 8:27 pm 
Offline
Member
Member
User avatar

Joined: Fri Jun 22, 2007 12:47 pm
Posts: 1598
Location: New Hampshire, USA
Thanks for the ideas & code. I'll sift through and hopefully come up with an 'article-like' entry. Also, the reason the 'movq' instruction issued you a warning/error is because I am working in 64-bit mode and was using the 'quad' suffix, using a plain 'mov' instruction will just default to the (largest?) general-purpose register size (which is fine for this). the FLDCW (IIRC) stands for FPU LoaD Control Word and has basic operations such as setting the default precision value.

I'll keep adding on to this wiki article, but I'd really like it if some more FPU-knowledgeable people could contribute as I have a feeling that my contributions will be somewhat limited.

_________________
Website: https://Joscor.com


Top
 Profile  
 
 Post subject: Re: FPU stub
PostPosted: Tue Nov 11, 2008 9:47 am 
Offline
Member
Member
User avatar

Joined: Fri Jun 22, 2007 12:47 pm
Posts: 1598
Location: New Hampshire, USA
Ok, I added a few explanations and commented the code.
I also threw in a reference page with alot of good info on the FPU.

_________________
Website: https://Joscor.com


Top
 Profile  
 
 Post subject: Re: FPU stub
PostPosted: Tue Nov 11, 2008 9:49 am 
Offline
Member
Member
User avatar

Joined: Sun Oct 22, 2006 7:01 am
Posts: 2646
Location: Devon, UK
Combuster wrote:
Then when the FPU is actually needed, the previous state is stored and the new state loaded.


I haven't done this yet, but out of interest, how do you know where to store the previous state? Do you simply keep an active pointer to the PCB of the last task that used the FPU?

Cheers,
Adam


Top
 Profile  
 
 Post subject: Re: FPU stub
PostPosted: Mon Nov 17, 2008 12:02 pm 
Offline
Member
Member

Joined: Fri Nov 09, 2007 3:30 am
Posts: 140
Location: Aalborg, Denmark
AJ wrote:
Combuster wrote:
Then when the FPU is actually needed, the previous state is stored and the new state loaded.


I haven't done this yet, but out of interest, how do you know where to store the previous state? Do you simply keep an active pointer to the PCB of the last task that used the FPU?


That's what the intel documents recommend. You can set the processor up so it only throws a #NM if the TS(Task Switched) bit is set. You can simply clear this bit when you change tasks if you use software multitasking. Then if you get an #NM and the task is another than the last one to invoke a #NM exception then you simply reload the math state from the new task after saving the old tasks mathstate and clear TS before returning from the interrupt

That's what I do and it works fine. You'll have to clear TS as the first operation in the interrupt procedure or else you'll get loads of problems(at least I had, #GPF's, #NM's, etc). Another thing. The 512 byte area where you save the mathstate to must be 16 byte aligned

_________________
http://j-software.dk | JPasKernel - My Object Pascal kernel


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 37 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group