Page 1 of 1

Mixing 64 and 32-bit code

Posted: Fri Nov 05, 2010 2:08 am
by rdos
From the Intel 64 and IA-32 Architectures Software Developpers Manual:
In IA-32e mode, bit 21 of the second doubleword of the segment
descriptor indicates whether a code segment contains native 64-bit
code. A value of 1 indicates instructions in this code segment are
executed in 64-bit mode. A value of 0 indicates the instructions in this
code segment are executed in compatibility mode. If L-bit is set, then
D-bit must be cleared. When not in IA-32e mode or for non-code
segments, bit 21 is reserved and should always be set to 0.
Does this mean that a 32-bit OS can execute "as-is" in long mode, just by avoiding ever setting the L bit in the code segment selector? Does it mean that if the OS sets up interrupt handlers in long-mode that have the L bit clear, they will work "as-is" in compability-mode? And would a page-fault handler with a clear L bit work regardless of the mode of the interrupted code? Is it that simple?

If it is, about the only piece of 64-bit software needed in the kernel would be some code that saves/loads 64-bit registers during task-switches. In a low end system, there would be no need to use more than 4G virtual address space, and so memory allocators for 64-bit applications could simply always give them a below 4G address, and then the whole kernel API would also work without translations (addresses usually passed in 32-bit registers would be equal to addresses passed in 64-bit registers).

Re: Mixing 64 and 32-bit code

Posted: Fri Nov 05, 2010 4:02 am
by JamesM
rdos wrote:From the Intel 64 and IA-32 Architectures Software Developpers Manual:
In IA-32e mode, bit 21 of the second doubleword of the segment
descriptor indicates whether a code segment contains native 64-bit
code. A value of 1 indicates instructions in this code segment are
executed in 64-bit mode. A value of 0 indicates the instructions in this
code segment are executed in compatibility mode. If L-bit is set, then
D-bit must be cleared. When not in IA-32e mode or for non-code
segments, bit 21 is reserved and should always be set to 0.
Does this mean that a 32-bit OS can execute "as-is" in long mode, just by avoiding ever setting the L bit in the code segment selector? Does it mean that if the OS sets up interrupt handlers in long-mode that have the L bit clear, they will work "as-is" in compability-mode? And would a page-fault handler with a clear L bit work regardless of the mode of the interrupted code? Is it that simple?

If it is, about the only piece of 64-bit software needed in the kernel would be some code that saves/loads 64-bit registers during task-switches. In a low end system, there would be no need to use more than 4G virtual address space, and so memory allocators for 64-bit applications could simply always give them a below 4G address, and then the whole kernel API would also work without translations (addresses usually passed in 32-bit registers would be equal to addresses passed in 64-bit registers).
No - some instructions are not available in 64-bit mode that are available in 32-bit mode. I don't have my manuals with me here, but from memory instructions like INC EAX occupy the same block of instruction space as the REX prefixes, so are not available.

Re: Mixing 64 and 32-bit code

Posted: Fri Nov 05, 2010 4:51 am
by rdos
JamesM wrote:
rdos wrote:From the Intel 64 and IA-32 Architectures Software Developpers Manual:
In IA-32e mode, bit 21 of the second doubleword of the segment
descriptor indicates whether a code segment contains native 64-bit
code. A value of 1 indicates instructions in this code segment are
executed in 64-bit mode. A value of 0 indicates the instructions in this
code segment are executed in compatibility mode. If L-bit is set, then
D-bit must be cleared. When not in IA-32e mode or for non-code
segments, bit 21 is reserved and should always be set to 0.
Does this mean that a 32-bit OS can execute "as-is" in long mode, just by avoiding ever setting the L bit in the code segment selector? Does it mean that if the OS sets up interrupt handlers in long-mode that have the L bit clear, they will work "as-is" in compability-mode? And would a page-fault handler with a clear L bit work regardless of the mode of the interrupted code? Is it that simple?

If it is, about the only piece of 64-bit software needed in the kernel would be some code that saves/loads 64-bit registers during task-switches. In a low end system, there would be no need to use more than 4G virtual address space, and so memory allocators for 64-bit applications could simply always give them a below 4G address, and then the whole kernel API would also work without translations (addresses usually passed in 32-bit registers would be equal to addresses passed in 64-bit registers).
No - some instructions are not available in 64-bit mode that are available in 32-bit mode. I don't have my manuals with me here, but from memory instructions like INC EAX occupy the same block of instruction space as the REX prefixes, so are not available.
But if I have a register-level API for 32-bit mode, it should be possible to pass these same registers from a 64-bit application that only uses the lower 32-bits of addresses? Of course, 64-bit code cannot pass integer values larger than 32-bit either, but I don't find that too problematic either (there is no API function today that needs to use more than one 32-bit register except for time-stamps). As far as I understand it, EAX is lower 32-bits of RAX and so on?

Just found some other problem with the idea (also from the Intel manual):
In legacy mode, the size of an IDT entry (16 bits or 32 bits) determines the size of
interrupt-stack-frame pushes. SS:ESP is pushed only on a CPL change. In 64-bit
mode, the size of interrupt stack-frame pushes is fixed at eight bytes. This is because
only 64-bit mode gates can be referenced. 64-bit mode also pushes SS:RSP unconditionally,
rather than only on a CPL change.
IOW, long-mode requires a new IDT that only can reference 64-bit code segments. It does sound pretty fatal to the concept, but perhaps not. The worse thing though is related to mode-switches. The only way I can see of switching from 64-bit to 32-bit mode is to do an iret. This requires pushing lots of thing onto the 64-bit stack, and there is too much overhead for the idea to be useful. It is also not allowed for a 32-bit application to use a 32-bit callgate to enter kernel. This would mean fatally degraded performance for syscalls (I have no idea why they made this limitation, it seems totally ad-hoc). :cry:

Re: Mixing 64 and 32-bit code

Posted: Fri Nov 05, 2010 5:03 am
by gerryg400
rdos wrote:Does this mean that a 32-bit OS can execute "as-is" in long mode, just by avoiding ever setting the L bit in the code segment selector? Does it mean that if the OS sets up interrupt handlers in long-mode that have the L bit clear, they will work "as-is" in compability-mode? And would a page-fault handler with a clear L bit work regardless of the mode of the interrupted code? Is it that simple?
No, interrupt handlers have to be 64 bit. However, I guess you could iret to a 32bit code segment to do the work.
rdos wrote:If it is, about the only piece of 64-bit software needed in the kernel would be some code that saves/loads 64-bit registers during task-switches. In a low end system, there would be no need to use more than 4G virtual address space, and so memory allocators for 64-bit applications could simply always give them a below 4G address, and then the whole kernel API would also work without translations (addresses usually passed in 32-bit registers would be equal to addresses passed in 64-bit registers).
Well yes, you would need 64bit isr stubs and 64bit, 4 level page-tables. Most everything else could be 32 bit.