X86-64: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (Fix lint errors)
 
(46 intermediate revisions by 36 users not shown)
Line 1: Line 1:
{{DISPLAYTITLE:x86-64}}
This article discusses '''x86-64''' CPUs (AMD64 and Intel's equivalent [[EM64T]] implementation). [[IA-64]] (Itanium) is '''really''' a different beast and not addressed here.
This article discusses '''x86-64''' CPUs (AMD64 and Intel's equivalent [[EM64T]] implementation). [[IA-64]] (Itanium) is '''really''' a different beast and not addressed here.



== Features ==
== Features ==


===What does Long Mode offer?===
===Long Mode===


Long mode extends general registers to 64 bits (RAX, RBX, RIP, RSP, RFLAGS, etc), and adds an additional 8 integer registers (R8, R9, ..., R15) plus 8 more SSE registers (XMM8 to XMM15) to the CPU. Linear addresses are extended to 64 bit (however, a given CPU may implement less than this) and the physical address space is extended to 52 bits (a given CPU may implement less than this). In essence long mode adds another mode to the CPU:
Long mode extends general registers to 64 bits (RAX, RBX, RIP, RSP, RFLAGS, etc), and adds eight additional integer registers (R8, R9, ..., R15) plus eight more SSE registers (XMM8 to XMM15) to the CPU. Linear addresses are extended to 64 bit (however, a given CPU may implement less than this) and the physical address space is extended to 52 bits (a given CPU may implement less than this). In essence long mode adds another mode to the CPU.
* Real mode
* Legacy mode (32 bit protected mode)
* Long mode (64 bit protected mode)
* System Management mode


Long mode does not support hardware task switching or virtual 8086 tasks, and most of the segment register details are ignored (a flat memory model is required). In long mode the current CS determines if the code currently running is 64 bit code (true long mode) or 32 bit code (compatibility mode), or even 16-bit protected mode code (still in compatibility mode).
Long mode does not support hardware task switching or virtual 8086 tasks. In long mode the current CS determines if the code currently running is 64 bit code (true long mode) or 32 bit code (compatibility mode), or even 16-bit protected mode code (still in compatibility mode). Using paging has become mandatory, and segmentation has been stripped down for performance reasons.


The first 64 bit CPUs from both Intel and AMD support 40 bit physical addresses and 48 bit linear addresses.
The first 64 bit CPUs from both Intel and AMD support 40 bit physical addresses and 48 bit linear addresses.


== Segmentation in Long Mode ==
=== Segmentation in Long Mode ===

Segmentation in long mode functions with a flat model with the exception of two registers: FS and GS. Setting the base address for these two segment registers is possible via two specific [[MSR|Model Specific Register (MSR)]]s, FS.base (C000_0100h) and GS.base (C000_0101h).


Additionally there is a long mode specific instruction called [[SWAPGS]], which swaps the contents of GS.base and another MSR called KernelGSBase (C000_0102h). This instruction is particularly useful for preserving kernel information for a specific logical processor core across context switches. '''Note: This is an exchange operation'''.
Segmentation in long mode functions with a flat model with the exception of two registers: FS and GS. Setting the base address for these two segment registers is possible via two specific MSRs, FS.base (C000_0100h)and GS.base (C000_0101h).


=== Further information ===
Additionally there is a long mode specific instruction called SWAPGS, which swaps the contents of GS.base and another MSR called KernelGSBase (C000_0102h). This instruction is particularly useful for preserving kernel information for a specific logical processor core across context switches. '''Note: This is an exchange operation'''.


:''This feature overview is incomplete. Please see the [[http://en.wikipedia.org/wiki/X86-64 Wikipedia article on x86-64]] for more information.''
Lastly it also important to note that any attempt to load a selector value into the FS or GS registers in long mode will automatically set their base addresses to zero due to the previously mentioned semantics of long mode segmentation.


==Setting up==
==Setting up==
Line 28: Line 26:
===How do I detect if the CPU is 64 bits ?===
===How do I detect if the CPU is 64 bits ?===


You can find that out by checking CPUID. All AMD64 compliant processors have the longmode-capable-bit turned on in the extended feature flags (bit 29) in EDX, after calling CPUID with EAX=0x80000001. There are also other bits required by long mode, but you can see those yourself in CPUID at [http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/24594.pdf AMD general purpose instruction reference]
After calling CPUID with EAX=0x80000001, all AMD64 compliant processors have the longmode-capable-bit turned on in the extended feature flags (bit 29) in EDX. There are also other bits required by long mode; you can check them out in the CPUID docs in the [http://support.amd.com/us/Processor_TechDocs/24594.pdf AMD general purpose instruction reference] (Link dead, the original author probably meant "AMD64 Architecture Programmer’s Manual Volume 3: General Purpose and System Instructions", found here: http://developer.amd.com/resources/developer-guides-manuals/)


==How do I enable Long Mode ?==
===How do I enable Long Mode ?===


The steps for enabling long mode are:
The steps for enabling long mode are:
* Have paging disabled
* Disable paging
* Set the PAE enable bit in CR4
* Set the PAE enable bit in CR4
* Load CR3 with the physical address of the PML4
* Load CR3 with the physical address of the PML4 (Level 4 Page Map)
* Enable long mode by setting the EFER.LME flag in MSR 0xC0000080
* Enable long mode by setting the LME flag (bit 8) in MSR 0xC0000080 (aka EFER)
* Enable paging
* Enable paging


''Reference: [https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.pdf Intel 64 and IA-32 Architectures Software Developer's Manual], Section 9.8.5''
Now the CPU will be in compatibility mode, and instructions are still 32-bit. To enter long mode, the D/B bit (bit 22, 2nd dword) of the GDT code segment must be clear (as it would be for a 16-bit code segment), and the L bit (bit 21, 2nd dword) of the GDT code segment must be set. Once that is done, the CPU is in 64-bit long mode.


Now the CPU will be in compatibility mode, and instructions are still 32-bit. To enter long mode, the D/B bit (bit 22, 2nd 32-bit value) of the GDT code segment must be clear (as it would be for a 16-bit code segment), and the L bit (bit 21, 2nd 32-bit value) of the GDT code segment must be set. Once that is done, the CPU is in 64-bit long mode.
== Are there restrictions on 32-bit code running in Legacy Mode ?==


=== Are there restrictions on 32-bit code running in Legacy Mode ?===
x86-64 processors can operate in a legacy mode, they still start in real mode and protected mode is still available (along with the associated v8086 mode). This means an x86 operating system, even DOS, will still run just fine. The only difference is that physical addresses can be up to 52 bits (or as many bits as implemented by the CPU) when PAE is used.


x86-64 processors can operate in a legacy mode, they still start in real mode and 16 and 32 bit protected mode is still available (along with the associated Virtual 8086 mode). This means an x86 operating system, even DOS, will still run just fine. The only difference is that physical addresses can be up to 52 bits (or as many bits as implemented by the CPU) when PAE is used.
However, there is nothing like [[Virtual 8086 Mode]] (16 bits support) once in long/compatibility mode.


However, [[Virtual 8086 Mode]] does not exist in long/compatibility mode.
==Entering Long Mode directly==

If you are running on a multi-processor system, you could send one processor a STARTUP IPI to a real mode memory address (see Intel MultiProcessor specification for more details) that loads a real mode program. The main problem with this approach is that it relies on multiple processors being present in the system.

===Entering Long Mode directly===


Protected mode must be entered before activating long mode. A minimal protected-mode environment must be established to allow long-mode initialization to take place. This environment must include the following:
Protected mode must be entered before activating long mode. A minimal protected-mode environment must be established to allow long-mode initialization to take place. This environment must include the following:
Line 55: Line 57:
:--''AMD64 docs, volume 2, section 14.4 (Enabling Protected Mode), 24593 Rev. 3.10 February 2005''
:--''AMD64 docs, volume 2, section 14.4 (Enabling Protected Mode), 24593 Rev. 3.10 February 2005''


That being said, [[Topic:11093|we have a thread]] where Brendan shows how you can enable 64-bit long mode with no 32-bit IDT and no 32-bit segments ... Be assured, however, that any paging-related exception that occurs in long mode before you enable 64-bit IDT will cause the processor to reset due to a triple fault ...
That being said, [[Topic:11093|we have a thread]] where Brendan shows how to enable 64-bit long mode with no 32-bit IDT and no 32-bit segments - be assured, however, that any paging-related exception that occurs in long mode before you enable 64-bit IDT will cause the processor to reset due to a triple fault.

There is also [[Entering Long Mode Directly|an example]] of this implemented in a [[bootloader]].
There is also [[Entering Long Mode Directly|an example]] of this implemented in a [[bootloader]].


== 64bit Environment Models ==
=== Notifying the BIOS ===
''The following discusses issues related to data types of high level languages. So, it's not relevant for people developing only in assembly.''


In order for the firmware built into the system to optimize itself for running in Long Mode, AMD recommends that the OS notify the BIOS about the intended target environment that the OS will be running in: 32-bit mode, 64-bit mode, or a mixture of both modes. This can be done by calling the BIOS interrupt 15h from Real Mode with AX set to 0xEC00, and BL set to 1 for 32-bit Protected Mode, 2 for 64-bit Long Mode, or 3 if both modes will be used.
There are three 64bit programming models you need to consider: LP64, ILP64, LLP64. Each model has its own pitfalls. The I/L/P stand for Int, Long, Pointer, respectively; and the 64 means thats how many bits in each.


== 64 bit Environment Models ==
This LP64 means Longs and Pointers are 64bits wide. LL is a special case and means long-long...


There are three 64 bit programming models you need to consider: LP64, ILP64, LLP64. Each model has its own pitfalls. The I/L/P stand for Int, Long, Pointer, respectively; the 64 is the number of bits in each.

LP64 means Longs (and Long Longs) and Pointers are 64 bits wide, Ints are 32 bits wide.
LLP64 means Long Longs and Pointers are 64 bits wide, Longs and Ints are 32 bit wide.
ILP64 means Ints, Longs (and Long Longs) and Pointers are 64 bit wide.

Most *nixes use the LP64 model, Windows uses the LLP64 convention. ILP64 is used very rarely.


=== Data Types ===
=== Data Types ===
Line 156: Line 165:
| LP64
| LP64
|}
|}

=== Text Segment Types ===

Another thing that you must keep in mind, that although the address space (and with it all the pointers) are 64 bit wide, the generated code in the text segment is most likely not. That's because by default gcc compiles to the "mov" instruction which has only 32 bit immediate. This means 64 bit programs are limited to 2G, just as 32 bit mode programs.

If you have ever seen an error message like this:
<syntaxhighlight lang="bash">
relocation truncated to fit: R_X86_64_32 against symbol
</syntaxhighlight>
then your code hit this barrier. For Assembly, you must use the "movabs" instruction instead of "mov", and for gcc you need to select a different text segment model with the "-mcmodel" argument.

{| {{wikitable}}
! Flag
! Text Segment Addressing
|-
| -mcmodel=small
| The program and its symbols must be linked in the lower 2 GB of the address space (this is the default model)
|-
| -mcmodel=large
| This model makes no assumptions about addresses and sizes of sections.
|-
| -mcmodel=medium
| The program is linked in the lower 2 GB of the address space. Small symbols are also placed there. Symbols with sizes larger than -mlarge-data-threshold are put into large data or bss sections and can be located above 2GB.
|-
| -mcmodel=kernel
| The kernel runs in the negative 2 GB of the address space. This model has to be used for Linux kernel code.
|}
It worth noting that code models are different for architectures, as they are tied with the instruction encoding. For example, AArch64 has a "-mcmodel=tiny" too, which allows 1M addressing, unknown to x86_64. And for AArch64 "-mcmodel=small" has a 4G limitation, not 2G as for the x86_64.


== See Also ==
== See Also ==
Line 161: Line 198:
* [[EM64T|Intel EM64T]]
* [[EM64T|Intel EM64T]]
* [[Creating a 64-bit kernel]]
* [[Creating a 64-bit kernel]]
* [[BOOTBOOT|BOOTBOOT bootloader]]
* [[User:Stephanvanschaik/Setting_Up_Long_Mode|Setting up long mode]]
* [[Limine|Limine bootloader]]
* [[X86-64 Instruction Encoding]]
* [[Setting_Up_Long_Mode|Setting up long mode]]


=== Wikipedia ===
=== Wikipedia ===
Line 167: Line 207:
* [[Wikipedia:64-bit|64-bit]]
* [[Wikipedia:64-bit|64-bit]]
=== External Links ===
=== External Links ===
* [http://www.amd.com/us-en/assets/content_type/DownloadableAssets/dwamd_AMD64_Porting_FAQ.pdf Porting to AMD64: FAQ],
* [http://www.amd.com/us-en/assets/content_type/DownloadableAssets/dwamd_AMD64_Porting_FAQ.pdf Porting to AMD64: FAQ]
* [http://www.amdboard.com/hammerspecial.html AMD64 Information]
* [http://www.amdboard.com/hammerspecial.html AMD64 Information]
* [http://www.x86-64.org/documentation.html x86-64 ABI and assembly guide]
* [http://www.x86-64.org/documentation.html x86-64 ABI and assembly guide]
* [http://downloads.openwatcom.org/ftp/devel/docs/elf-64-gen.pdf ELF-64 Object File Format(direct PDF link)]
* [http://downloads.openwatcom.org/ftp/devel/docs/elf-64-gen.pdf ELF-64 Object File Format (direct PDF link)]
* [http://stackoverflow.com/questions/1753602/registers-for-x86-64-processors StackOverflow x86_64 register assignment]


[[Category:X86 CPU]]
[[Category:X86 CPU]]
[[Category:X86-64]]
[[Category:Operating Modes]]
[[Category:Operating Modes]]

[[de:Long Mode]]

Latest revision as of 15:56, 9 June 2024

This article discusses x86-64 CPUs (AMD64 and Intel's equivalent EM64T implementation). IA-64 (Itanium) is really a different beast and not addressed here.

Features

Long Mode

Long mode extends general registers to 64 bits (RAX, RBX, RIP, RSP, RFLAGS, etc), and adds eight additional integer registers (R8, R9, ..., R15) plus eight more SSE registers (XMM8 to XMM15) to the CPU. Linear addresses are extended to 64 bit (however, a given CPU may implement less than this) and the physical address space is extended to 52 bits (a given CPU may implement less than this). In essence long mode adds another mode to the CPU.

Long mode does not support hardware task switching or virtual 8086 tasks. In long mode the current CS determines if the code currently running is 64 bit code (true long mode) or 32 bit code (compatibility mode), or even 16-bit protected mode code (still in compatibility mode). Using paging has become mandatory, and segmentation has been stripped down for performance reasons.

The first 64 bit CPUs from both Intel and AMD support 40 bit physical addresses and 48 bit linear addresses.

Segmentation in Long Mode

Segmentation in long mode functions with a flat model with the exception of two registers: FS and GS. Setting the base address for these two segment registers is possible via two specific Model Specific Register (MSR)s, FS.base (C000_0100h) and GS.base (C000_0101h).

Additionally there is a long mode specific instruction called SWAPGS, which swaps the contents of GS.base and another MSR called KernelGSBase (C000_0102h). This instruction is particularly useful for preserving kernel information for a specific logical processor core across context switches. Note: This is an exchange operation.

Further information

This feature overview is incomplete. Please see the [Wikipedia article on x86-64] for more information.

Setting up

How do I detect if the CPU is 64 bits ?

After calling CPUID with EAX=0x80000001, all AMD64 compliant processors have the longmode-capable-bit turned on in the extended feature flags (bit 29) in EDX. There are also other bits required by long mode; you can check them out in the CPUID docs in the AMD general purpose instruction reference (Link dead, the original author probably meant "AMD64 Architecture Programmer’s Manual Volume 3: General Purpose and System Instructions", found here: http://developer.amd.com/resources/developer-guides-manuals/)

How do I enable Long Mode ?

The steps for enabling long mode are:

  • Disable paging
  • Set the PAE enable bit in CR4
  • Load CR3 with the physical address of the PML4 (Level 4 Page Map)
  • Enable long mode by setting the LME flag (bit 8) in MSR 0xC0000080 (aka EFER)
  • Enable paging

Reference: Intel 64 and IA-32 Architectures Software Developer's Manual, Section 9.8.5

Now the CPU will be in compatibility mode, and instructions are still 32-bit. To enter long mode, the D/B bit (bit 22, 2nd 32-bit value) of the GDT code segment must be clear (as it would be for a 16-bit code segment), and the L bit (bit 21, 2nd 32-bit value) of the GDT code segment must be set. Once that is done, the CPU is in 64-bit long mode.

Are there restrictions on 32-bit code running in Legacy Mode ?

x86-64 processors can operate in a legacy mode, they still start in real mode and 16 and 32 bit protected mode is still available (along with the associated Virtual 8086 mode). This means an x86 operating system, even DOS, will still run just fine. The only difference is that physical addresses can be up to 52 bits (or as many bits as implemented by the CPU) when PAE is used.

However, Virtual 8086 Mode does not exist in long/compatibility mode.

If you are running on a multi-processor system, you could send one processor a STARTUP IPI to a real mode memory address (see Intel MultiProcessor specification for more details) that loads a real mode program. The main problem with this approach is that it relies on multiple processors being present in the system.

Entering Long Mode directly

Protected mode must be entered before activating long mode. A minimal protected-mode environment must be established to allow long-mode initialization to take place. This environment must include the following:

  • A protected-mode IDT for vectoring interrupts and exceptions to the appropriate handlers while in protected mode.
  • The protected-mode interrupt and exception handlers referenced by the IDT.
  • Gate descriptors for each handler must be loaded in the IDT.
--AMD64 docs, volume 2, section 14.4 (Enabling Protected Mode), 24593 Rev. 3.10 February 2005

That being said, we have a thread where Brendan shows how to enable 64-bit long mode with no 32-bit IDT and no 32-bit segments - be assured, however, that any paging-related exception that occurs in long mode before you enable 64-bit IDT will cause the processor to reset due to a triple fault.

There is also an example of this implemented in a bootloader.

Notifying the BIOS

In order for the firmware built into the system to optimize itself for running in Long Mode, AMD recommends that the OS notify the BIOS about the intended target environment that the OS will be running in: 32-bit mode, 64-bit mode, or a mixture of both modes. This can be done by calling the BIOS interrupt 15h from Real Mode with AX set to 0xEC00, and BL set to 1 for 32-bit Protected Mode, 2 for 64-bit Long Mode, or 3 if both modes will be used.

64 bit Environment Models

There are three 64 bit programming models you need to consider: LP64, ILP64, LLP64. Each model has its own pitfalls. The I/L/P stand for Int, Long, Pointer, respectively; the 64 is the number of bits in each.

LP64 means Longs (and Long Longs) and Pointers are 64 bits wide, Ints are 32 bits wide. LLP64 means Long Longs and Pointers are 64 bits wide, Longs and Ints are 32 bit wide. ILP64 means Ints, Longs (and Long Longs) and Pointers are 64 bit wide.

Most *nixes use the LP64 model, Windows uses the LLP64 convention. ILP64 is used very rarely.

Data Types

This table lists the breakdown of sizes in the various programming models.

Datatype LP64 ILP64 LLP64 ILP32 LP32
char 8 8 8 8 8
short 16 16 16 16 16
_int 32 -- 32 -- --
int 32 64 32 32 16
long 64 64 32 32 32
long long -- -- 64 -- --
pointer 64 64 64 32 32

Models used by 64bit OSs

The following table lists what some current 64bit OS have as a programming model.

OS Mode
Windows XP X64 LLP64
Linux LP64
FreeBSD/OpenBSD LP64
Solaris LP64
DEC OSF/1 Alpha LP64
SGI Irix LP64
HP UX 11 LP64

Text Segment Types

Another thing that you must keep in mind, that although the address space (and with it all the pointers) are 64 bit wide, the generated code in the text segment is most likely not. That's because by default gcc compiles to the "mov" instruction which has only 32 bit immediate. This means 64 bit programs are limited to 2G, just as 32 bit mode programs.

If you have ever seen an error message like this:

relocation truncated to fit: R_X86_64_32 against symbol

then your code hit this barrier. For Assembly, you must use the "movabs" instruction instead of "mov", and for gcc you need to select a different text segment model with the "-mcmodel" argument.

Flag Text Segment Addressing
-mcmodel=small The program and its symbols must be linked in the lower 2 GB of the address space (this is the default model)
-mcmodel=large This model makes no assumptions about addresses and sizes of sections.
-mcmodel=medium The program is linked in the lower 2 GB of the address space. Small symbols are also placed there. Symbols with sizes larger than -mlarge-data-threshold are put into large data or bss sections and can be located above 2GB.
-mcmodel=kernel The kernel runs in the negative 2 GB of the address space. This model has to be used for Linux kernel code.

It worth noting that code models are different for architectures, as they are tied with the instruction encoding. For example, AArch64 has a "-mcmodel=tiny" too, which allows 1M addressing, unknown to x86_64. And for AArch64 "-mcmodel=small" has a 4G limitation, not 2G as for the x86_64.

See Also

Articles

Wikipedia

External Links