OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 11:30 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 6 posts ] 
Author Message
 Post subject: [SOLVED] Triple Fault on Setting SS Register
PostPosted: Thu Dec 23, 2021 7:19 am 
Offline

Joined: Tue May 26, 2020 8:44 pm
Posts: 9
Hello all,

I've been getting a triple fault when trying to set the SS register after loading my GDT. I was getting a bunch of very strange GP faults when working on interrupts, and realized I hadn't set the segment registers after creating and loading my GDT. When I tried to set them, it works for all of the registers except the SS register. The OS is 64-bit, already in long mode, and loaded via UEFI. I'm running it on a VirtualBox machine. I don't want to flood the post with too much code, but here's my GDT loading function

Code:
GDTCodeOrDataEntry* initializeGDT(PhysicalMemoryLayout* physMemLayout) {
   GDTCodeOrDataEntry* gdt = (GDTCodeOrDataEntry*) AllocatePage(physMemLayout);

   gdt[0] = (GDTCodeOrDataEntry) { 0, 0, 0, 0, 0, 0 }; //Null entry

   // Initialize table with "default" entries
   for(int i = 1; i < GDT_BASE_ENTRIES; i++) {
      gdt[i] = basicGDTEntry();
   }

   // Define kernel/userspace code segments
   // Note: Default entries work for both data segments, as priviledge levels
   //       are ignored in 64-bit mode data segments
   gdt[GDT_KERNEL_CODE_ARRAY_INDEX].accessByte |= ACCESS_EXECUTABLE_FLAG;
   gdt[GDT_KERNEL_CODE_ARRAY_INDEX].limitPartTwoAndFlags |= LONG_MODE_CODE_FLAG;
   gdt[GDT_USER_CODE_ARRAY_INDEX].accessByte |= ACCESS_EXECUTABLE_FLAG | ACCESS_USERSPACE_FLAG;
   gdt[GDT_USER_CODE_ARRAY_INDEX].limitPartTwoAndFlags |= LONG_MODE_CODE_FLAG;

   // Set up GDTR data
   GDTRegister gdtr;
   gdtr.limit = GDT_BASE_ENTRIES * sizeof(GDTCodeOrDataEntry) - 1;
   gdtr.baseAddress = (UInt64) gdt;

   // Load GDT and jump to new kernel code 'segment'
   asm volatile goto (
      "lgdt %0\n\t"
      "mov %2,%%rax\n\t"
      "mov %%rax,%%ds\n\t"
      "mov %%rax,%%es\n\t"
      "mov %%rax,%%fs\n\t"
      "mov %%rax,%%gs\n\t"
      "mov %%rax,%%ss\n\t"
      "pushq %1\n\t"
      "pushq $%l3\n\t"
      "retfq\n\t"
      : /* No output */
      : "m"(gdtr), "r"(GDT_KERNEL_CODE_SELECTOR_INDEX), "r"(GDT_KERNEL_DATA_SELECTOR_INDEX)
      : "rax", "memory", "cc"
      : finish_lgdt
   );

   finish_lgdt:

   return gdt;
}


Here's the state of the registers at crash time:

Code:
rax=0000000000000010 rbx=0000000000062020 rcx=0000000000000010 rdx=0000000000000008
rsi=0000000000041028 rdi=0000000000000061 r8 =000000000000007d r9 =00000000dfa082ce
r10=0000000000000078 r11=00000000dfa082ce r12=00000000de71f698 r13=0000000000000109
r14=0000000000000000 r15=0000000000000000 iopl=0 nv up di pl nz na pe nc
rip=0000000000402257 rsp=00000000df237830 rbp=00000000df237870
cs=0038 ds=0010 es=0010 fs=0010 gs=0010 ss=0030                     rflags=00000002
%0000000000402257 8e d0                   mov ss, eax


You can see it actually triple faulted right on that mov instruction, and that the other segment registers were set properly. Lastly, here's VirtualBox's dump of my GDT on the crash:

Code:
0008 CodeEO Bas=00000000 Lim=000fffff DPL=0 P  NA       AVL=0 L=1
0010 DataRO Bas=00000000 Lim=000fffff DPL=0 P  A        AVL=0 L=0
0018 CodeEO Bas=00000000 Lim=000fffff DPL=3 P  NA       AVL=0 L=1
0020 DataRO Bas=00000000 Lim=000fffff DPL=0 P  NA       AVL=0 L=0


I believe all the entries look good there. I even set all of the fields that the AMD manuals say are ignored in long mode, just in case. I'm sure the issue is something silly, I'm probably just being blind, but I've spent way too much time trying to figure this out. I'm happy to provide any other info you might need. Thanks ahead of time for any help!


Last edited by Tyyrus on Thu Dec 23, 2021 10:29 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Triple Fault on Setting SS Register
PostPosted: Thu Dec 23, 2021 9:08 am 
Offline
Member
Member

Joined: Sat Jul 02, 2016 7:02 am
Posts: 207
Is the W bit set in the Type field of the GDT descriptor for the data segment?


Top
 Profile  
 
 Post subject: Re: Triple Fault on Setting SS Register
PostPosted: Thu Dec 23, 2021 9:18 am 
Offline

Joined: Tue May 26, 2020 8:44 pm
Posts: 9
linuxyne wrote:
Is the W bit set in the Type field of the GDT descriptor for the data segment?


I tried it both ways and nothing changed, but the AMD64 documentation says it's ignored anyway in long mode:

Quote:
Segment-limit checking is not performed on any data segments in 64-bit mode, and both the segment-
limit field and granularity (G) bit are ignored. The D/B bit is unused in 64-bit mode

The expand-down (E), writable (W), and accessed (A) type-field attributes are ignored


Top
 Profile  
 
 Post subject: Re: Triple Fault on Setting SS Register
PostPosted: Thu Dec 23, 2021 9:33 am 
Offline
Member
Member

Joined: Sat Jul 02, 2016 7:02 am
Posts: 207
Tyyrus wrote:
but the AMD64 documentation says it's ignored anyway in long mode

True, but on IvyBridge kvm and on the QEMU's TCG, without the W bit, the machines throw a GPF. QEMU's helper_load_seg function checks for DESC_W_MASK when handling a write into R_SS. QEMU's own bios code also takes care of setting this bit (and a few others) despite it being marked as ignored in the long mode. I haven't tested on bare-metal, though.

Since you are already in 64-bit long mode, and one can see that the SS is set to 0x30 (perhaps by the UEFI), one can compare the bits between that descriptor and the one you are trying to set. This should immediately expose any differences between the two descriptors.

Edit: From the Intel manuals: "Loading the SS register with a segment selector for a nonwritable data segment generates a general-protection exception (#GP)."


Last edited by linuxyne on Thu Dec 23, 2021 9:53 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Triple Fault on Setting SS Register
PostPosted: Thu Dec 23, 2021 9:53 am 
Offline
Member
Member
User avatar

Joined: Sat Mar 31, 2012 3:07 am
Posts: 4591
Location: Chichester, UK
Is the Segment Present flag in the segment descriptor set?


Top
 Profile  
 
 Post subject: Re: Triple Fault on Setting SS Register
PostPosted: Thu Dec 23, 2021 10:28 am 
Offline

Joined: Tue May 26, 2020 8:44 pm
Posts: 9
linuxyne wrote:
True, but on IvyBridge kvm and on the QEMU's TCG, without the W bit, the machines throw a GPF. QEMU's helper_load_seg function checks for DESC_W_MASK when handling a write into R_SS. QEMU's own bios code also takes care of setting this bit (and a few others) despite it being marked as ignored in the long mode. I haven't tested on bare-metal, though.

Since you are already in 64-bit long mode, and one can see that the SS is set to 0x30 (perhaps by the UEFI), one can compare the bits between that descriptor and the one you are trying to set. This should immediately expose any differences between the two descriptors.

Edit: From the Intel manuals: "Loading the SS register with a segment selector for a nonwritable data segment generates a general-protection exception (#GP)."


Thank you, problem solved after comparing my GDT to the one set by UEFI! It was an issues with the W bit after all. I had tried setting it, but I had mistakenly used the wrong index and set it for the kernel code segment instead. I didn't pay much attention to it, since the AMD documentation said the bit was supposed to be ignored anyway. In the future I'll keep in mind that it might not always be accurate (especially with VMs) and check both that and the Intel manuals. Thanks again!


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

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 79 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