Virtualbox, how to force a specific CPU to the guest

32,912

Solution 1

VirtualBox and CPUID basics

You need to set the VBoxInternal/CPUM/HostCPUID extradata of the virtual machine. This will make VirtualBox report custom results for the CPUID instruction to the guest. Depending on the value of the EAX register, this instruction returns information about the processor - things like vendor, type, family, stepping, brand, cache size, features (MMX, SSE, SSE2, PAE, HTT), etc. The more results you mangle, the higher the chances to fool the guest.

You can use the vboxmanage setextradata command to configure the virtual machine. For example,

vboxmanage setextradata WinXP VBoxInternal/CPUM/HostCPUID/80000003/ebx 0x50202952

will make CPUID return 50202952₍₁₆₎ in the EBX register, when called with EAX set to 80000003₍₁₆₎. (From now on, hexadecimal numbers will be written as 0xNN or NNh.)

Setting the CPU vendor string

If EAX is 0 (or 80000000h on AMD), CPUID returns the vendor as an ASCII string in registers EBX, EDX, ECX (notice the order). For an AMD CPU, they look like this:

| Register | Value      | Description                    |
|----------|------------|--------------------------------|
| EBX      | 6874_7541h | The ASCII characters "h t u A" |
| ECX      | 444D_4163h | The ASCII characters "D M A c" |
| EDX      | 6974_6E65h | The ASCII characters "i t n e" |

(Taken from AMD CPUID Specification, subsection "CPUID Fn0000_0000_E")

If you concatenate EBX, EDX and ECX, you'll get AuthenticAMD.

If you have Bash and the traditional Unix utilities, you can easily set the vendor with the following commands:

vm='WinXP'  # UUID works as well
# The vendor string needs to have 12 characters!
vendor='AuthenticAMD'
if [ ${#vendor} -ne 12 ]; then
    exit 1
fi
ascii2hex() { echo -n 0x; od -A n --endian little -t x4 | sed 's/ //g'; }

registers=(ebx edx ecx)
for (( i=0; i<${#vendor}; i+=4 )); do
    register=${registers[$(($i/4))]}
    value=`echo -n "${vendor:$i:4}" | ascii2hex`
    # set value to an empty string to reset the CPUID, i.e.
    # value=""
    for eax in 00000000 80000000; do
        key=VBoxInternal/CPUM/HostCPUID/${eax}/${register}
        vboxmanage setextradata "$vm" $key $value
    done
done

Setting the CPU brand string

If EAX is 80000002h, 80000003h, 80000004h, CPUID returns 16 ASCII characters of the brand string in registers EAX, EBX, ECX, EDX, totaling 3 * 16 = 48 characters; the string is terminated with a null character. Note that this feature was introduced with Pentium 4 processors. This is how the brand string can look on a Pentium 4 processor:

| EAX Input Value | Return Values   | ASCII Equivalent |
|-----------------|-----------------|------------------|
| 80000002h       | EAX = 20202020h | "    "           |
|                 | EBX = 20202020h | "    "           |
|                 | ECX = 20202020h | "    "           |
|                 | EDX = 6E492020h | "nI  "           |
|-----------------|-----------------|------------------|
| 80000003h       | EAX = 286C6574h | "(let"           |
|                 | EBX = 50202952h | "P )R"           |
|                 | ECX = 69746E65h | "itne"           |
|                 | EDX = 52286D75h | "R(mu"           |
|-----------------|-----------------|------------------|
| 80000004h       | EAX = 20342029h | " 4 )"           |
|                 | EBX = 20555043h | " UPC"           |
|                 | ECX = 30303531h | "0051"           |
|                 | EDX = 007A484Dh | "☠zHM"           |
|-----------------|-----------------|------------------|

(Taken from Intel Architecture Instruction Set Extensions Programming Reference, subsection 2.9, "CPUID Instruction", table 2-30. ☠ is the null character (numerical value 0).)

If you put the results together, you'll get Intel(R) Pentium(R) 4 CPU 1500MHz☠.

If you have Bash and the traditional Unix utilities, you can easily set the brand with the following commands:

vm='WinXP'  # UUID works as well
# The brand string needs to have 47 characters!
# The null terminator is added automatically
brand='              Intel(R) Pentium(R) 4 CPU 1500MHz'
if [ ${#brand} -ne 47 ]; then
    exit 1
fi
ascii2hex() { echo -n 0x; od -A n --endian little -t x4 | sed 's/ //g'; }

eax_values=(80000002 80000003 80000004)
registers=(edx ecx ebx eax)
for (( i=0; i<${#brand}; i+=4 )); do
    eax=${eax_values[$((${i} / 4 / 4))]}
    register=${registers[$((${i} / 4 % 4 ))]}
    key=VBoxInternal/CPUM/HostCPUID/${eax}/${register}
    value=`echo -n "${brand:$i:4}" | ascii2hex`
    # set value to an empty string to reset the CPUID, i.e.
    # value=""
    vboxmanage setextradata "$vm" $key $value
done

If you have a Windows command prompt, you can set the brand to Intel(R) Core(TM)2 CPU 6600 @ 2.40 GHz1 by running:

set vm=your-vm-name-or-uuid
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000002/eax 0x65746e49
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000002/ebx 0x2952286c
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000002/ecx 0x726f4320
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000002/edx 0x4d542865
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000003/eax 0x43203229
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000003/ebx 0x20205550
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000003/ecx 0x20202020
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000003/edx 0x20202020
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000004/eax 0x30303636
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000004/ebx 0x20402020
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000004/ecx 0x30342e32
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000004/edx 0x007a4847

Computer: Intel(R) Core(TM)2 CPU 6600 @ 2.40 GHz

1 The HostCPUID values were taken from VirtualBox bug report #7865.

Solution 2

Here's an approach which allows masquerading the host CPU precisely as a specific CPU rather than try to second-guess the necessary settings. You'll need access to a machine running VirtualBox on that host CPU so you can dump its cpuid registers (it's probably best to choose an architecture which is reasonably similar to that of your actual CPU as model). If you don't have one to hand, you can ask around (I've had success on Reddit for example).

  1. Create a "model" file from the CPU you'd like to emulate:

    vboxmanage list hostcpuids > i7_6600U
    
  2. On the target host, ensure the VM you want to modify isn't running; you may want to take a backup just in case.
  3. Run the following script to load the model file (i7_6600U here) into the definition of your VBox VM (my_vm_name here):

    #!/bin/bash
    vm=my_vm_name
    model_file=i7_6600U
    
    egrep -e '^[[:digit:]abcdef]{8} ' $model_file |
    while read -r line; do
        leaf="0x`echo $line | cut -f1 -d' '`"
        # VBox doesn't like applying leaves between the below boundaries so skip those:
        if [[ $leaf -lt 0x0b || $leaf -gt 0x17 ]]; then
            echo "Applying: $line"
            vboxmanage modifyvm $vm --cpuidset $line
        fi
    done
    
  4. That's it, you can now run your VM and enjoy the masqueraded CPU (note: you only need to run the above script once).

If you ever need to rollback the CPU masquerade, you can use vboxmanage modifyvm $vm --cpuidremove $leaf for each of the leaves in the above loop (man vboxmanage is your friend).

This has been working flawlessly for a couple of months for me, masquerading a Kaby Lake CPU (i7_7500U) as a Skylake one (i7_6600U) on an Ubuntu 17.04 host running VBox 5.1.22. The approach should work on any host OS, provided you can create an equivalent of the little bash script above for that OS.

Share:
32,912

Related videos on Youtube

IUnknown
Author by

IUnknown

Code for food, game for entertainment, listen to music and read for enlightenment. That's as much as I know about me.

Updated on September 18, 2022

Comments

  • IUnknown
    IUnknown over 1 year

    I have a XP guest in VirtualBox, windows 8 host. The guest shows the processor transparently same as the host (i5 2500k). However most of the installers don't recognize this processors and fail to continue stating non-supported processor.

    Is there a way to fool the guest into thinking this is old processor? If I recalll correctly VMWare had a CPU masking feature, is there something similar in virtualbox?

    • Admin
      Admin almost 11 years
      What software are you installing that checks the CPU model?
    • Admin
      Admin almost 11 years
      Double Agent controls, Orca and Wix. This is for a VB6 project which we are trying to revive.
  • Rob Zombie
    Rob Zombie over 7 years
    Thanks. Got my VM broken with VERR_CFGM_INVALID_CHILD_PATH.
  • Rob Zombie
    Rob Zombie over 7 years
    sed has to be used with G flag: 's/ //g'
  • sxc731
    sxc731 about 7 years
    What a great answer! And with the advent of Kaby Lake CPUs, this has suddenly become most interesting due to a certain policy from Redmond to not support Windows 7 on those... It seems that all that's missing are the instructions on how to set "EAX=1: Processor Info and Feature Bits" as they aren't simple strings (with just the CPU brand, CPU-Z still recognizes the CPU as KL); anyone knows?
  • Mark Amery
    Mark Amery about 7 years
    Per forums.virtualbox.org/viewtopic.php?f=2&t=77211#p359428, there may be a better (more succinct, more supported) way of setting these values.
  • Cristian Ciupitu
    Cristian Ciupitu about 7 years
    @MarkAmery, thanks for the link. It work fine for the brand, but not so fine for the vendor because the vendor isn't set in the EAX register and the --cpuid subcommand requires a value for the EAX register.
  • abulhol
    abulhol almost 7 years
    Regarding the part "Setting the CPU vendor string", I have a comment: You must make sure to change the vendor name to "AuthenticAMD" on AMD CPU and "GenuineIntel" on Intel CPU. If you use "GenuineIntel" on an AMD CPU, the virtual machine will break very probably.
  • njbair
    njbair almost 7 years
    @sxc731 did you ever get a good answer on how to fool a Windows 7 VM? In my case I have to make my Ryzen look like an Opteron.
  • sxc731
    sxc731 almost 7 years
    @njbair I've worked it out myself and have posted the answer below. Enjoy!
  • njbair
    njbair almost 7 years
    Thanks so much for this! It worked like a charm on my Ryzen 7700K. I used an old AMD Socket SF2 motherboard to get the CPUID by running an Ubuntu LiveCD, installing VirtualBox in the live environment, and running the vboxmanage command. On the host side, I installed the Windows Subsystem for Linux so I could run a Bash prompt in Windows 10 and run the script you shared. Now I'm able to trick my Windows 7 VM into thinking I'm running an A8-5600K.
  • sxc731
    sxc731 almost 7 years
    Glad this works for you too! I should have clarified that none of this requires a Linux host; not even collecting the "model" file, all it needs is VirtualBox running on that machine. The bash script may look complicated but all it does is read the lines off the model file, skipping leaves between 0000000b and 00000017 (inclusive) and running them one by one through vboxmanage modifyvm my_vm_name --cpuidset <line> so this can easily be done by hand as it's a one-off.
  • njbair
    njbair almost 7 years
    No biggie, I had WSL installed on the host computer anyway, and the Ubuntu LiveCD was just because it was the quickest way to spin up the old AMD motherboard.