Page 1 of 1

WRMSR causes triple fault

Posted: Wed Sep 22, 2004 8:43 pm
by oswizard
Okay... this is a very strange problem that has been puzzling me for days. I am trying to write to the MSRs necessary to set up SYSENTER/SYSEXIT system calls, and when I call wrmsr, the computer triple faults. (my WriteMSR is included below).

The strange problem about this is that my interrupt handling is known to work. If I place an "int 3" before the wrmsr, my kernel dumps the register states to the screen. EDX:EAX and ECX match what I would expect for the wrmsr instruction. If I place the "int 3" after the wrmsr instruction, the triple fault occurs. There is plenty of stack space (ie. it is not a kmode stack overflow triple fault).

Furthermore, it works fine under Bochs and on a Pentium 3 processor. The actual system call also works fine. It does not work on all the Pentium 4 computers that I have at home, but I cannot find any documentation in the Intel manuals about changes in MSR functionality. Interrupts are disabled and it is ring 0 code.

Also, according to the documentation, a #GP is generated if the MSR number is out of range or if reserved bits are set. I know that it is not GPF'ing since I successfully receive a GPF if I intentionally choose an invalid MSR address.

The register states reported by the int3 instruction are absolutely identical on both a working machine and a nonworking machine:

Code: Select all

IoDumpContext() : ISR 0x03, code 0x00000000
EAX=0x803007FE ECX=0x00000176 EDX=0x00000000 EBX=0x00003A10 ESP=0x80006FBC
EBP=0x80006FEC ESI=0x00003A10 EDI=0x00000000 EIP=0x80302EBC EFL=0x00000046
CR0=0x80000011 CR1=0x00000000 CR2=0x00000000 CR3=0x00100000 CR4=0x00000000
CS=0x0008 DS=0x0010 ES=0x0010 FS=0x0010 GS=0x0010 SS=0x0010
My GDT is set up correctly (ring 0 code, ring 0 data, ring 3 code, ring 3 data), since everything works fine on another computer and on bochs.

Code: Select all

; void WriteMSR(unsigned int msr, unsigned int lowval)
_WriteMSR@8:
   mov      ecx, dword ptr [esp+4]
   mov      eax, dword ptr [esp+8]
   xor      edx, edx
   wrmsr
   ret      8
Sorry about the long read, but I wanted to ensure I gave enough information to solve the problem. It seems very suspicious to me. I want to know if anyone can see anything obviously wrong with this, or if you think something else is corrupt. Any ideas, even "did you turn your computer on"-type questions are fine, since I feel I am overlooking the obvious.

Thanks in advance.

Re:WRMSR causes triple fault

Posted: Thu Sep 23, 2004 12:22 am
by Candy
all I can think about at this time is that

- You're in VM86 mode. Then the instruction would be invalid
- You're writing them in a different order than Microsoft does, and Intel didn't test this order. Did you set the CS and ESP MSRs?
- The MSRs might only be defined after you enable access to SYSENTER/EXIT. I doubt this myself but it might be a thing to look at. -> scratch that, no bit to be found
- You have some very weird P4 that doesn't support SYSENTER. Try looking at standard feature flags, bit 11


There's no logical explanation. Can we see more of the code around the wrmsr, or can you explain the state of the CPU at that time in more detail (pmode, rmode, whatever)?

Re:WRMSR causes triple fault

Posted: Thu Sep 23, 2004 6:50 am
by oswizard
Well, I've had some time to do more testing. wrmsr works if it is among the first instructions to be executed in my kernel. Therefore, I think I have some kind of corruption occurring. Furthermore, if I place a "jmp $" afterward, instead of a "int3", the computer does not triple fault.

What I don't understand though is why wrmsr seems to cause my interrupt handlers to no longer work. Perhaps something happens that causes my stack to grow downward overwriting something important. I'll try creating a TSS-based double fault handler, invalidate the last page of my stack, and see if something happens.

Thanks for your help. At least now I can rule out something stupid I overlooked.

Re:WRMSR causes triple fault

Posted: Thu Sep 23, 2004 10:06 pm
by oswizard
Well, I've resolved my problem. For the record, I was forgetting to do a invlpg after unmapping the linear 1-1 mapping of low memory. I have not the slightest clue why this would be a problem, but it is certainly something that could be processor dependant.

So anyways, thanks for the reassurance that I wasn't overlooking something blatantly obvious, such as transposing EDX and EAX, for example.

Re:WRMSR causes triple fault

Posted: Fri Sep 24, 2004 3:04 pm
by Dreamsmith
Ah, yes. It is vitally important that any alteration to any page table entry be followed immediately by INVLPG. I have a little function like this:

Code: Select all

INLINE void setPageTableEntry(int entry, UInt32 paddr, unsigned flags)
{
   ((UInt32*)PAGE_TAB_VADDR)[entry] = paddr | flags;
   invlpg((UInt32)entry * PAGESIZE);
}
...and I never alter a page table entry except through the use of this (or a couple of similar) functions, just to make sure I don't forget. Otherwise, bad things happen, bad things that are very, very difficult to trace down, as they appear in some other random spot for no apparent reason.