x86-64: canonical addresses and actual available r

2019-05-07 11:18发布

问题:

Intel and AMD documentation says that for 64 bit mode only 48 bits are actually available for virtual addresses, and bits from 48 to 63 must replicate bit 47 (sign-extension). As far as I know, all current CPU are implemented this way, but nothing (in theory) forbids to extend the available space in future implementations (and this won't break the binary compatibility).

Is there a standard way to programatically determine the number of meaningful bits? (i.e. some specific CPUID, as happens for physical addresses).

I know that in practice 48 bits are far more than enough for any reasonable application and OS; my question is theoretical.

回答1:

Yes, you can use CPUID.80000008H:EAX[7:0] if supported.

The algorithm is as follow:

  1. Check the maximum extended E value for cpuid with CPUID.80000000h.EAX.
  2. If E >= 80000008h, use CPUID.80000008H:EAX[7:0] for the number of physical address bits.
    CPUID.80000008H:EAX[15:8] for the number of linear address bits.
  3. Else if CPUID.1:EDX.PAE (bit 6) then the CPU has 36 physical address bits and 32 linear address bits.
  4. Else the CPU has 32 physical and logical address bits.

The notation, from Intel, CPUID.X:R B means

  • X The value to put into eax before cpuid.
  • R The output register of interest.
  • B A bitfield in the form [upper:lower] inclusive, or a named bit in the form .bitname.

AMD set the maximum limits for virtual:physical addresses at 63:52.
The current implementation is typically 48:40, though the size of the physical address space can be different.


An example code that can be compiled with NASM

BITS 64

GLOBAL max_phy_addr
GLOBAL max_lin_addr


SECTION .text


max_phy_addr:
 push rbx


 mov eax, 80000000h
 cpuid

 cmp eax, 80000008h
 jae .fromCpuid

 mov eax, 1
 cpuid

 mov eax, 32
 shr edx, 4
 and edx, 4

 add eax, edx

 pop rbx
 ret

.fromCpuid:

 mov eax, 80000008h
 cpuid

 movzx eax, al

 pop rbx
 ret


max_lin_addr:
 push rbx

 mov eax, 80000000h
 cpuid

 cmp eax, 80000008h
 jae .fromCpuid

 mov eax, 1
 cpuid

 mov eax, 32

 pop rbx
 ret

.fromCpuid:

 mov eax, 80000008h
 cpuid

 movzx eax, ah

 pop rbx
 ret

And used with a C program such as

#include <stdio.h>

long max_phy_addr();
long max_lin_addr();

int main()
{
   printf("Phy: %llu\nLin: %llu\n", max_phy_addr(), max_lin_addr());
   return 0;
}

And I've just discovered that my Haswell is a 48:39!