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)?
FPU stub
FPU stub
Website: https://joscor.com
- piranha
- Member
- Posts: 1391
- Joined: Thu Dec 21, 2006 7:42 pm
- Location: Unknown. Momentum is pretty certain, however.
- Contact:
Re: FPU stub
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):
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
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?
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
dbittman on IRC, @danielbittman on twitter
https://dbittman.github.io
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: FPU stub
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.
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
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).
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: Select all
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
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: Select all
; 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 SSE, you should write the XF handler as well (for SIMD exceptions).
Re: FPU stub
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.
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
Re: FPU stub
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.
I also threw in a reference page with alot of good info on the FPU.
Website: https://joscor.com
Re: FPU stub
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?Combuster wrote:Then when the FPU is actually needed, the previous state is stored and the new state loaded.
Cheers,
Adam
Re: FPU stub
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 interruptAJ wrote: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?Combuster wrote:Then when the FPU is actually needed, the previous state is stored and the new state loaded.
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