Setting segment registers in long mode illegal?
Setting segment registers in long mode illegal?
Is it illegal? When I try to do it, it triple faults immediately.
Do segment registers serve any purpose in long mode?
Do segment registers serve any purpose in long mode?
-
- Member
- Posts: 5512
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Setting segment registers in long mode illegal?
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:Is it illegal? When I try to do it, it triple faults immediately.
In compatibility mode, segment registers behave the same way they do in protected mode.angods wrote:Do segment registers serve any purpose in long 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?
My GDT is exactly the same as in https://wiki.osdev.org/Setting_Up_Long_Mode, so I'm sure it's valid.Octocontrabass wrote: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:Is it illegal? When I try to do it, it triple faults immediately.
In compatibility mode, segment registers behave the same way they do in protected mode.angods wrote:Do segment registers serve any purpose in long 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.
But I just realised it only causes a triple fault when modifying ss.
Re: Setting segment registers in long mode illegal?
The 'LONG_MODE' flag should not be set for data segment descriptors. It's only used to indicate a 64-bit code segment:
Try removing it and see if it changes anything.
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)
Re: Setting segment registers in long mode illegal?
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.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:
Try removing it and see if it changes anything.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)
Code: Select all
xor ax, ax
mov ss, ax
Re: Setting segment registers in long mode illegal?
I don't think it makes any difference, does it? Isn't that bit just ignored for data segment descriptors?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:
Try removing it and see if it changes anything.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)
Re: Setting segment registers in long mode illegal?
I'm pretty sure that only the system should set SS to 0 (during interrupts). In any case, you are just masking the problem.angods wrote: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.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:
Try removing it and see if it changes anything.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)
Code: Select all
xor ax, ax mov ss, ax
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?
It can't be '0' though, because IRETQ throws #GP when it's 0. I must find a different solution.angods wrote: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.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:
Try removing it and see if it changes anything.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)
Code: Select all
xor ax, ax mov ss, ax
Re: Setting segment registers in long mode illegal?
Yes it can be 0. It's the way that the processor marks nested interrupts in long mode.angods wrote:It can't be '0' though, because IRETQ throws #GP when it's 0. I must find a different solution.
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?
iansjack wrote:I'm pretty sure that only the system should set SS to 0 (during interrupts). In any case, you are just masking the problem.angods wrote: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.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:
Try removing it and see if it changes anything.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)
Code: Select all
xor ax, ax mov ss, ax
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:
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
Re: Setting segment registers in long mode illegal?
I try to reference intel's manual(s) whenever I can, but finding some information is hard.iansjack wrote:Yes it can be 0. It's the way that the processor marks nested interrupts in long mode.angods wrote:It can't be '0' though, because IRETQ throws #GP when it's 0. I must find a different solution.
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?
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
-
- Posts: 14
- Joined: Sat Oct 10, 2020 4:05 pm
Re: Setting segment registers in long mode illegal?
You are currently using
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.
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)
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?
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?
For the L bit, the manual specifically says:iansjack wrote:I don't think it makes any difference, does it? Isn't that bit just ignored for data segment descriptors?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:
Try removing it and see if it changes anything.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)
Also, the access and flag bits for the data segment are wrong. It should be:When not in IA-32e mode or for non-code segments, bit 21 is reserved and should always be set to 0.
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)
-
- Member
- Posts: 5512
- Joined: Mon Mar 25, 2013 7:01 pm
Re: Setting segment registers in long mode illegal?
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.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 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.