Setting segment registers in long mode illegal?

Programming, for all ages and all languages.
Post Reply
angods
Member
Member
Posts: 26
Joined: Sat Oct 23, 2021 5:36 am

Setting segment registers in long mode illegal?

Post by angods »

Is it illegal? When I try to do it, it triple faults immediately.

Do segment registers serve any purpose in long mode?
Octocontrabass
Member
Member
Posts: 5513
Joined: Mon Mar 25, 2013 7:01 pm

Re: Setting segment registers in long mode illegal?

Post by Octocontrabass »

angods wrote:Is it illegal? When I try to do it, it triple faults immediately.
Most instructions to set segment registers work the same in 64-bit long mode as they do in protected mode, including the part where they load a segment descriptor into the hidden descriptor part of the segment register. A triple fault indicates an issue with your GDT, your LDT, your choice of selector, or your choice of instruction.
angods wrote:Do segment registers serve any purpose in long mode?
In compatibility mode, segment registers behave the same way they do in protected mode.

In 64-bit mode, most functions of segmentation go away, but some of them still work. CS still determines the current privilege level. FS and GS still have base addresses. SS is given a new function as a flag to indicate privilege level changes.
angods
Member
Member
Posts: 26
Joined: Sat Oct 23, 2021 5:36 am

Re: Setting segment registers in long mode illegal?

Post by angods »

Octocontrabass wrote:
angods wrote:Is it illegal? When I try to do it, it triple faults immediately.
Most instructions to set segment registers work the same in 64-bit long mode as they do in protected mode, including the part where they load a segment descriptor into the hidden descriptor part of the segment register. A triple fault indicates an issue with your GDT, your LDT, your choice of selector, or your choice of instruction.
angods wrote:Do segment registers serve any purpose in long mode?
In compatibility mode, segment registers behave the same way they do in protected mode.

In 64-bit mode, most functions of segmentation go away, but some of them still work. CS still determines the current privilege level. FS and GS still have base addresses. SS is given a new function as a flag to indicate privilege level changes.
My GDT is exactly the same as in https://wiki.osdev.org/Setting_Up_Long_Mode, so I'm sure it's valid.

But I just realised it only causes a triple fault when modifying ss.
User avatar
deadmutex
Member
Member
Posts: 85
Joined: Wed Sep 28, 2005 11:00 pm

Re: Setting segment registers in long mode illegal?

Post by deadmutex »

The 'LONG_MODE' flag should not be set for data segment descriptors. It's only used to indicate a 64-bit code segment:

Code: Select all

    .Data: equ $ - GDT
        dd 0xFFFF                                   ; Limit & Base (low)
        db 0                                        ; Base (mid)
        db PRESENT | NOT_SYS | RW | 0xF             ; Access
        db GRAN_4K | LONG_MODE                      ; Flags
        db 0                                        ; Base (high)
Try removing it and see if it changes anything.
angods
Member
Member
Posts: 26
Joined: Sat Oct 23, 2021 5:36 am

Re: Setting segment registers in long mode illegal?

Post by angods »

deadmutex wrote:The 'LONG_MODE' flag should not be set for data segment descriptors. It's only used to indicate a 64-bit code segment:

Code: Select all

    .Data: equ $ - GDT
        dd 0xFFFF                                   ; Limit & Base (low)
        db 0                                        ; Base (mid)
        db PRESENT | NOT_SYS | RW | 0xF             ; Access
        db GRAN_4K | LONG_MODE                      ; Flags
        db 0                                        ; Base (high)
Try removing it and see if it changes anything.
It didn't change anything. I found a 'solution' though. Apparently, ss can be set to 0x0 (null segment). I'm not sure if it's ok to do that though.

Code: Select all

xor ax, ax
mov ss, ax
User avatar
iansjack
Member
Member
Posts: 4688
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Setting segment registers in long mode illegal?

Post by iansjack »

deadmutex wrote:The 'LONG_MODE' flag should not be set for data segment descriptors. It's only used to indicate a 64-bit code segment:

Code: Select all

    .Data: equ $ - GDT
        dd 0xFFFF                                   ; Limit & Base (low)
        db 0                                        ; Base (mid)
        db PRESENT | NOT_SYS | RW | 0xF             ; Access
        db GRAN_4K | LONG_MODE                      ; Flags
        db 0                                        ; Base (high)
Try removing it and see if it changes anything.
I don't think it makes any difference, does it? Isn't that bit just ignored for data segment descriptors?
User avatar
iansjack
Member
Member
Posts: 4688
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Setting segment registers in long mode illegal?

Post by iansjack »

angods wrote:
deadmutex wrote:The 'LONG_MODE' flag should not be set for data segment descriptors. It's only used to indicate a 64-bit code segment:

Code: Select all

    .Data: equ $ - GDT
        dd 0xFFFF                                   ; Limit & Base (low)
        db 0                                        ; Base (mid)
        db PRESENT | NOT_SYS | RW | 0xF             ; Access
        db GRAN_4K | LONG_MODE                      ; Flags
        db 0                                        ; Base (high)
Try removing it and see if it changes anything.
It didn't change anything. I found a 'solution' though. Apparently, ss can be set to 0x0 (null segment). I'm not sure if it's ok to do that though.

Code: Select all

xor ax, ax
mov ss, ax
I'm pretty sure that only the system should set SS to 0 (during interrupts). In any case, you are just masking the problem.

What value are you trying to use for SS, are you in kernel or user mode, and are you sure it refers to a valid data selector?
angods
Member
Member
Posts: 26
Joined: Sat Oct 23, 2021 5:36 am

Re: Setting segment registers in long mode illegal?

Post by angods »

angods wrote:
deadmutex wrote:The 'LONG_MODE' flag should not be set for data segment descriptors. It's only used to indicate a 64-bit code segment:

Code: Select all

    .Data: equ $ - GDT
        dd 0xFFFF                                   ; Limit & Base (low)
        db 0                                        ; Base (mid)
        db PRESENT | NOT_SYS | RW | 0xF             ; Access
        db GRAN_4K | LONG_MODE                      ; Flags
        db 0                                        ; Base (high)
Try removing it and see if it changes anything.
It didn't change anything. I found a 'solution' though. Apparently, ss can be set to 0x0 (null segment). I'm not sure if it's ok to do that though.

Code: Select all

xor ax, ax
mov ss, ax
It can't be '0' though, because IRETQ throws #GP when it's 0. I must find a different solution.
User avatar
iansjack
Member
Member
Posts: 4688
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Setting segment registers in long mode illegal?

Post by iansjack »

angods wrote:It can't be '0' though, because IRETQ throws #GP when it's 0. I must find a different solution.
Yes it can be 0. It's the way that the processor marks nested interrupts in long mode.

I get the impression that you are just trying things at random that you have seen on the Internet (including this forum). Have you actually read the manufacturer's Programmer's Manual on the subject?
angods
Member
Member
Posts: 26
Joined: Sat Oct 23, 2021 5:36 am

Re: Setting segment registers in long mode illegal?

Post by angods »

iansjack wrote:
angods wrote:
deadmutex wrote:The 'LONG_MODE' flag should not be set for data segment descriptors. It's only used to indicate a 64-bit code segment:

Code: Select all

    .Data: equ $ - GDT
        dd 0xFFFF                                   ; Limit & Base (low)
        db 0                                        ; Base (mid)
        db PRESENT | NOT_SYS | RW | 0xF             ; Access
        db GRAN_4K | LONG_MODE                      ; Flags
        db 0                                        ; Base (high)
Try removing it and see if it changes anything.
It didn't change anything. I found a 'solution' though. Apparently, ss can be set to 0x0 (null segment). I'm not sure if it's ok to do that though.

Code: Select all

xor ax, ax
mov ss, ax
I'm pretty sure that only the system should set SS to 0 (during interrupts). In any case, you are just masking the problem.

What value are you trying to use for SS, are you in kernel or user mode, and are you sure it refers to a valid data selector?

Code: Select all

PRESENT        equ 1 << 7
NOT_SYS        equ 1 << 4
EXEC           equ 1 << 3
DC             equ 1 << 2
RW             equ 1 << 1
ACCESSED       equ 1 << 0
 
GRAN_4K       equ 1 << 7
SZ_32         equ 1 << 6
LONG_MODE     equ 1 << 5

GlobalDescriptorTable64:
	dw GDTEnd64 - GDTStart64 - 1
	dq GDTStart64
GDTStart64:
	GDTEntry64_0: dd 0, 0 ;NULL
	GDTEntry64_1:
		dd 0xFFFF                           
		db 0                                 
		db PRESENT | NOT_SYS | EXEC | RW | 0xF
		db GRAN_4K | LONG_MODE    
		db 0                       
	GDTEntry64_2:
	         dd 0xFFFF                                  
                 db 0                                       
                 db PRESENT | NOT_SYS | RW | 0xF
                 db GRAN_4K
                 db 0                                        
GDTEnd64:
I can never be 100% sure, but this is the code.

Code: Select all

    	lgdt [GlobalDescriptorTable64]
        jmp 0x8:LongModeMain ;Where 0x8 is code segment

Code: Select all

LongModeMain:
	mov ax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	xor ax, ax ;If I don't xor it, it crashes
	mov ss, ax
I'm in kernel mode. I mean.. I don't even know. I didn't do anything with permission level yet..
angods
Member
Member
Posts: 26
Joined: Sat Oct 23, 2021 5:36 am

Re: Setting segment registers in long mode illegal?

Post by angods »

iansjack wrote:
angods wrote:It can't be '0' though, because IRETQ throws #GP when it's 0. I must find a different solution.
Yes it can be 0. It's the way that the processor marks nested interrupts in long mode.

I get the impression that you are just trying things at random that you have seen on the Internet (including this forum). Have you actually read the manufacturer's Programmer's Manual on the subject?
I try to reference intel's manual(s) whenever I can, but finding some information is hard.

And the Intel ISA manual mentions that 'If the stack segment selector is NULL going back to compatibility mode,' #GP is thrown.

https://www.felixcloutier.com/x86/iret:iretd
Doctor5555
Posts: 14
Joined: Sat Oct 10, 2020 4:05 pm

Re: Setting segment registers in long mode illegal?

Post by Doctor5555 »

You are currently using

Code: Select all

.Data: equ $ - GDT
        dd 0xFFFF                                   ; Limit & Base (low)
        db 0                                        ; Base (mid)
        db PRESENT | NOT_SYS | RW | 0xF             ; Access
        db GRAN_4K | LONG_MODE                      ; Flags
        db 0                                        ; Base (high)
The '| 0xF' on the Access byte of that data segment descriptor may be setting a bit that should be zero - the AMD manual suggests that bit 11 in the upper dword should be 0 and bit 12 should be 1, and my code triple faults when I have bit 11 set. Bit 12 is set correctly by NOT_SYS.
You may also want to remove the '| 0xF' from your code segment descriptor to make the code cleaner, although it will probably have no effect on the outcome.
'0x0000920000000000' works for me as the full data segment entry, and you seem to be using '0x00A09F000000FFFF', while removing the '| 0xF' should give you '0x00A092000000FFFF', which should at least work. Please correct me if I've mis-counted the bytes somewhere there.
You can view the full AMD manual here, and the relevant section is Volume 2 Chapter 4 sections 7 (legacy) and 8 (long mode). I'd recommend bookmarking that link, and I've found the AMD manuals to be easier to navigate than Intel's, although that is probably just personal preference.

If this works for you, the wiki page should probably be updated.
nexos
Member
Member
Posts: 1078
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: Setting segment registers in long mode illegal?

Post by nexos »

One thing I want to add is that I used to ask questions about basic x86 stuff here. I soon realized that the only way to truly understand x86 is to look at the manuals. I have to thank @iansjack for pointing me to the manuals instead of just giving me the answer here. Trust, me it works!
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
User avatar
deadmutex
Member
Member
Posts: 85
Joined: Wed Sep 28, 2005 11:00 pm

Re: Setting segment registers in long mode illegal?

Post by deadmutex »

iansjack wrote:
deadmutex wrote:The 'LONG_MODE' flag should not be set for data segment descriptors. It's only used to indicate a 64-bit code segment:

Code: Select all

    .Data: equ $ - GDT
        dd 0xFFFF                                   ; Limit & Base (low)
        db 0                                        ; Base (mid)
        db PRESENT | NOT_SYS | RW | 0xF             ; Access
        db GRAN_4K | LONG_MODE                      ; Flags
        db 0                                        ; Base (high)
Try removing it and see if it changes anything.
I don't think it makes any difference, does it? Isn't that bit just ignored for data segment descriptors?
For the L bit, the manual specifically says:
When not in IA-32e mode or for non-code segments, bit 21 is reserved and should always be set to 0.
Also, the access and flag bits for the data segment are wrong. It should be:

Code: Select all

    .Data: equ $ - GDT
        dd 0xFFFF                       ; Limit & Base (low)
        db 0                            ; Base (mid)
        db PRESENT | NOT_SYS | RW | DC  ; Access
        db GRAN_4K | SZ_32              ; Flags
        db 0                            ; Base (high)
EDIT: Fixed formatting
Octocontrabass
Member
Member
Posts: 5513
Joined: Mon Mar 25, 2013 7:01 pm

Re: Setting segment registers in long mode illegal?

Post by Octocontrabass »

Doctor5555 wrote:The '| 0xF' on the Access byte of that data segment descriptor may be setting a bit that should be zero - the AMD manual suggests that bit 11 in the upper dword should be 0 and bit 12 should be 1, and my code triple faults when I have bit 11 set. [...] If this works for you, the wiki page should probably be updated.
The wiki page needs to be updated either way. The 0xF is supposed to be the upper 4 bits of the limit, which is part of the flags byte, not the access byte.

The AMD manual gives the impression that only the present bit matters for data segments, but I can't say the same for Intel. However, both manuals do say that system calls require particular descriptors for SS, so you should match your descriptor for SS with the descriptor used by SYSCALL/SYSENTER.
Post Reply