Page 1 of 1

Setting segment registers in long mode illegal?

Posted: Sun Oct 31, 2021 5:50 pm
by angods
Is it illegal? When I try to do it, it triple faults immediately.

Do segment registers serve any purpose in long mode?

Re: Setting segment registers in long mode illegal?

Posted: Sun Oct 31, 2021 6:38 pm
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.

Re: Setting segment registers in long mode illegal?

Posted: Sun Oct 31, 2021 6:59 pm
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.

Re: Setting segment registers in long mode illegal?

Posted: Sun Oct 31, 2021 9:35 pm
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.

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 4:49 am
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

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 5:18 am
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?

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 5:23 am
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?

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 5:29 am
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.

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 5:32 am
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?

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 5:48 am
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..

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 6:08 am
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

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 8:44 am
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.

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 9:26 am
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!

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 9:32 am
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

Re: Setting segment registers in long mode illegal?

Posted: Mon Nov 01, 2021 11:12 am
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.