Loading content...
When Intel announced VT-x in 2005, AMD was not far behind. AMD introduced AMD-V (AMD Virtualization), also known as SVM (Secure Virtual Machine), as their hardware virtualization extension. While both technologies solve the same fundamental problem—enabling efficient isolation of virtual machines—AMD took a different architectural approach that offers its own advantages.
Understanding AMD-V is essential for several reasons: it's the foundation for hypervisors running on AMD processors (which power a significant portion of cloud infrastructure), it influences how cross-platform hypervisors like KVM are designed, and its unique features (particularly Nested Page Tables) were actually introduced before Intel's equivalent EPT, driving the entire industry forward.
By the end of this page, you will understand AMD's SVM (Secure Virtual Machine) architecture, the VMCB (Virtual Machine Control Block) structure, world switch operations, AMD's approach to nested paging, and the key differences between AMD-V and Intel VT-x. You'll be equipped to understand how hypervisors leverage AMD hardware.
AMD's virtualization technology is officially called SVM (Secure Virtual Machine), reflecting AMD's emphasis on security isolation in addition to performance. The architecture introduces similar concepts to Intel's VMX but with different terminology and implementation details.
Key Components of SVM:
| AMD Term | Intel Equivalent | Description |
|---|---|---|
| SVM | VMX | Overall virtualization extension |
| VMCB | VMCS | Guest configuration/state structure |
| Guest Mode | VMX Non-Root | Execution mode for virtual machines |
| Host Mode | VMX Root | Execution mode for hypervisor |
| #VMEXIT | VM Exit | Transition from guest to hypervisor |
| VMRUN | VMLAUNCH/VMRESUME | Instruction to enter guest mode |
| Nested Page Tables (NPT) | Extended Page Tables (EPT) | Hardware memory virtualization |
The fundamental philosophy is the same: create a hardware-enforced boundary between the hypervisor and guests, with automatic state switching and controlled exit points when guests perform sensitive operations.
Enabling SVM:
Before using SVM features, the hypervisor must verify support and enable the capability:
int enable_svm(void) {
uint32_t eax, ebx, ecx, edx;
uint64_t vm_cr, efer;
/* Check CPUID for SVM support */
__cpuid(0x80000001, eax, ebx, ecx, edx);
if (!(ecx & (1 << 2))) {
return -ENOTSUP; /* SVM not supported */
}
/* Check if SVM is disabled by BIOS */
vm_cr = rdmsr(MSR_VM_CR);
if (vm_cr & SVM_VM_CR_SVM_DIS_MASK) {
return -EPERM; /* SVM locked out */
}
/* Enable SVM in EFER */
efer = rdmsr(MSR_EFER);
efer |= EFER_SVME;
wrmsr(MSR_EFER, efer);
return 0;
}
AMD-V was originally codenamed 'Pacifica' during development. You may encounter this term in older documentation, academic papers, or hypervisor source code comments. SVM is the official architectural name.
The VMCB (Virtual Machine Control Block) is the central data structure in AMD-V, analogous to Intel's VMCS. However, there's a critical difference: the VMCB is a directly accessible memory structure, unlike the VMCS which requires special instructions (VMREAD/VMWRITE) for access.
This direct accessibility makes AMD-V programming arguably simpler—the hypervisor can read and write VMCB fields using normal memory operations. No special opcodes needed.
VMCB Layout:
The VMCB is a 4KB page-aligned structure divided into two main areas:
VMCB State Save Area:
The state save area contains all guest-visible CPU state. Unlike Intel's VMCS where state is exchanged through special instructions, AMD's approach saves/restores state directly to/from these memory locations:
struct vmcb_state_save {
/* Segment Registers (each with selector, attrib, limit, base) */
struct vmcb_seg es; /* 0x400 */
struct vmcb_seg cs; /* 0x410 */
struct vmcb_seg ss; /* 0x420 */
struct vmcb_seg ds; /* 0x430 */
struct vmcb_seg fs; /* 0x440 */
struct vmcb_seg gs; /* 0x450 */
struct vmcb_seg gdtr; /* 0x460 */
struct vmcb_seg ldtr; /* 0x470 */
struct vmcb_seg idtr; /* 0x480 */
struct vmcb_seg tr; /* 0x490 */
uint8_t reserved1[43]; /* 0x4A0 */
uint8_t cpl; /* 0x4CB - Current Privilege Level */
uint32_t reserved2; /* 0x4CC */
uint64_t efer; /* 0x4D0 - Extended Feature Enable */
uint8_t reserved3[112]; /* 0x4D8 */
uint64_t cr4; /* 0x548 */
uint64_t cr3; /* 0x550 */
uint64_t cr0; /* 0x558 */
uint64_t dr7; /* 0x560 */
uint64_t dr6; /* 0x568 */
uint64_t rflags; /* 0x570 */
uint64_t rip; /* 0x578 */
uint8_t reserved4[88]; /* 0x580 */
uint64_t rsp; /* 0x5D8 */
uint8_t reserved5[24]; /* 0x5E0 */
uint64_t rax; /* 0x5F8 */
uint64_t star; /* 0x600 */
uint64_t lstar; /* 0x608 */
uint64_t cstar; /* 0x610 */
uint64_t sfmask; /* 0x618 */
uint64_t kernel_gs_base; /* 0x620 */
uint64_t sysenter_cs; /* 0x628 */
uint64_t sysenter_esp; /* 0x630 */
uint64_t sysenter_eip; /* 0x638 */
uint64_t cr2; /* 0x640 */
uint8_t reserved6[32]; /* 0x648 */
uint64_t g_pat; /* 0x668 - Guest PAT */
uint64_t dbgctl; /* 0x670 */
uint64_t br_from; /* 0x678 */
uint64_t br_to; /* 0x680 */
uint64_t lastexcpfrom; /* 0x688 */
uint64_t lastexcpto; /* 0x690 */
};
Notice how straightforward VMCB programming is compared to Intel's VMCS. To set the guest's instruction pointer, you simply write: vmcb->save.rip = 0x7C00. No special instructions required. This directness makes AMD-V hypervisors easier to understand and debug.
AMD-V provides extensive control over which guest operations trigger #VMEXITs. The intercept control fields in the VMCB are bitmaps where each bit corresponds to a specific operation type.
CR Intercepts (offset 0x000):
Control register access can be intercepted selectively. Bits 0-15 control read intercepts for CR0-CR15, and bits 16-31 control write intercepts:
/* Intercept CR0 and CR4 writes, but allow reads */
vmcb->control.intercept_cr =
(1 << 16) | /* CR0 write */
(1 << 20); /* CR4 write */
DR Intercepts (offset 0x004):
Similar structure for debug registers. Useful for implementing guest debugging support or hiding host debug state:
/* Intercept all DR accesses for debugging */
vmcb->control.intercept_dr = 0xFFFFFFFF;
Exception Intercepts (offset 0x008):
32-bit bitmap where each bit corresponds to an exception vector (0-31). Setting a bit causes that exception type to exit:
/* Intercept page faults (#PF) and debug exceptions (#DB) */
vmcb->control.intercept_exceptions =
(1 << 1) | /* #DB - Debug */
(1 << 14); /* #PF - Page Fault */
| Bit | Name | Description |
|---|---|---|
| 0 | INTERCEPT_INTR | Intercept INTR (external interrupts) |
| 1 | INTERCEPT_NMI | Intercept NMI |
| 2 | INTERCEPT_SMI | Intercept SMI |
| 3 | INTERCEPT_INIT | Intercept INIT signal |
| 4 | INTERCEPT_VINTR | Intercept virtual INTR |
| 7 | INTERCEPT_CPUID | Intercept CPUID (always recommended) |
| 10 | INTERCEPT_INVD | Intercept INVD instruction |
| 12 | INTERCEPT_HLT | Intercept HLT instruction |
| 13 | INTERCEPT_INVLPG | Intercept INVLPG instruction |
| 14 | INTERCEPT_INVLPGA | Intercept INVLPGA instruction |
| 15 | INTERCEPT_IOIO_PROT | Use I/O permission map |
| 16 | INTERCEPT_MSR_PROT | Use MSR permission map |
| 17 | INTERCEPT_TASK_SWITCH | Intercept task switches |
| 18 | INTERCEPT_FERR_FREEZE | Intercept FERR_FREEZE |
| 19 | INTERCEPT_SHUTDOWN | Intercept shutdown events |
I/O Permission Map:
Rather than intercepting all I/O, AMD-V uses a 12KB bitmap (8KB for port addresses 0x0000-0xFFFF, 4KB reserved). Each bit represents one port:
void setup_io_bitmap(struct vmcb *vmcb, uint8_t *io_bitmap) {
/* Allocate 12KB aligned bitmap */
memset(io_bitmap, 0xFF, 12 * 1024); /* Block all by default */
/* Allow direct access to specific ports */
clear_io_intercept(io_bitmap, 0x80); /* Debug port */
/* Set VMCB to use bitmap */
vmcb->control.intercept |= INTERCEPT_IOIO_PROT;
vmcb->control.iopm_base_pa = virt_to_phys(io_bitmap);
}
void clear_io_intercept(uint8_t *bitmap, uint16_t port) {
bitmap[port / 8] &= ~(1 << (port % 8));
}
MSR Permission Map:
Similar concept for MSR access. 8KB total covering all MSR ranges. Two bits per MSR: one for read, one for write.
MSR Permission Map Layout (8KB):
Offset 0x0000-0x07FF: MSRs 0x00000000-0x00001FFF (read)
Offset 0x0800-0x0FFF: MSRs 0x00000000-0x00001FFF (write)
Offset 0x1000-0x17FF: MSRs 0xC0000000-0xC0001FFF (read)
Offset 0x1800-0x1FFF: MSRs 0xC0000000-0xC0001FFF (write)
Every intercept adds exit overhead. The art of hypervisor tuning is enabling only necessary intercepts. For example, intercepting every page fault would destroy performance. With NPT/EPT, most page faults can be handled by hardware, and only nested page table violations need VMM attention.
The VMRUN instruction is AMD's mechanism for transitioning from host mode to guest mode. Unlike Intel's separate VMLAUNCH/VMRESUME, AMD uses a single instruction for all guest entries.
VMRUN Execution:
; RAX must contain physical address of VMCB
mov rax, [vmcb_phys_addr]
vmrun rax
; Execution returns here after #VMEXIT
When VMRUN executes, the processor performs an atomic world switch:
Save Host State: Certain host registers are saved to an internal Host Save Area (configured via MSR_VM_HSAVE_PA)
Load Guest State: All guest state from VMCB state save area is loaded into CPU registers
Clear Intercept State: Internal flags are set based on VMCB intercept controls
Switch to Guest Mode: The CPU begins executing at guest RIP
Host Save Area:
Unlike Intel where host state is explicitly in the VMCS, AMD uses a separate Host Save Area pointed to by an MSR:
void init_host_save_area(void) {
void *hsave = alloc_page();
wrmsr(MSR_VM_HSAVE_PA, virt_to_phys(hsave));
}
The processor automatically saves/restores these host registers:
#VMEXIT Processing:
When a #VMEXIT occurs:
The hypervisor then reads the VMCB to determine exit cause:
void handle_vmexit(struct vmcb *vmcb) {
uint64_t exitcode = vmcb->control.exitcode;
uint64_t exitinfo1 = vmcb->control.exitinfo1;
uint64_t exitinfo2 = vmcb->control.exitinfo2;
switch (exitcode) {
case VMEXIT_CR0_WRITE:
handle_cr0_write(vmcb, exitinfo1);
break;
case VMEXIT_IOIO:
handle_io(vmcb, exitinfo1, exitinfo2);
break;
case VMEXIT_MSR:
handle_msr(vmcb, exitinfo1);
break;
case VMEXIT_CPUID:
handle_cpuid(vmcb);
break;
case VMEXIT_NPF: /* Nested Page Fault */
handle_npf(vmcb, exitinfo1, exitinfo2);
break;
case VMEXIT_VMRUN:
case VMEXIT_VMLOAD:
case VMEXIT_VMSAVE:
/* Nested virtualization */
handle_nested_virt(vmcb, exitcode);
break;
default:
panic("Unhandled VMEXIT: %llx", exitcode);
}
}
AMD's single VMRUN instruction (versus Intel's VMLAUNCH + VMRESUME) simplifies hypervisor code. There's no need to track whether a guest has been launched before—every entry uses VMRUN. The VMCB's clean bits mechanism provides the optimization that Intel achieves via separate instructions.
AMD-V defines a comprehensive set of exit codes that tell the hypervisor exactly why the guest exited. The exit code is a 64-bit value, though currently only the lower bits are defined.
Common Exit Code Categories:
| Code | Name | Description |
|---|---|---|
| 0x00-0x0F | VMEXIT_CRx_READ | Read from CR0-CR15 (when intercepted) |
| 0x10-0x1F | VMEXIT_CRx_WRITE | Write to CR0-CR15 (when intercepted) |
| 0x20-0x2F | VMEXIT_DRx_READ | Read from DR0-DR15 (when intercepted) |
| 0x30-0x3F | VMEXIT_DRx_WRITE | Write to DR0-DR15 (when intercepted) |
| 0x40-0x5F | VMEXIT_EXCP_n | Exception n (0-31) occurred |
| 0x60 | VMEXIT_INTR | Physical INTR intercepted |
| 0x61 | VMEXIT_NMI | NMI intercepted |
| 0x62 | VMEXIT_SMI | SMI intercepted |
| 0x64 | VMEXIT_VINTR | Virtual interrupt pending |
| 0x72 | VMEXIT_CPUID | CPUID instruction |
| 0x76 | VMEXIT_HLT | HLT instruction |
| 0x78 | VMEXIT_INVLPG | INVLPG instruction |
| 0x7B | VMEXIT_IOIO | I/O instruction |
| 0x7C | VMEXIT_MSR | MSR access (RDMSR/WRMSR) |
| 0x7F | VMEXIT_SHUTDOWN | Shutdown condition (triple fault) |
| 0x80 | VMEXIT_VMRUN | VMRUN instruction (nested virt) |
| 0x81 | VMEXIT_VMMCALL | VMMCALL (hypercall) |
| 0x400 | VMEXIT_NPF | Nested Page Fault |
Exit Information Fields:
The exit information format depends on the exit type:
For I/O Exits (VMEXIT_IOIO):
EXITINFO1 contains:
void handle_io(struct vmcb *vmcb, uint64_t info1, uint64_t info2) {
uint16_t port = info1 & 0xFFFF;
bool is_in = (info1 >> 16) & 1;
bool is_string = (info1 >> 17) & 1;
uint8_t size = 1 << ((info1 >> 19) & 7);
if (is_in) {
vmcb->save.rax = emulate_port_read(port, size);
} else {
emulate_port_write(port, vmcb->save.rax, size);
}
/* Advance RIP (manual for I/O exits) */
vmcb->save.rip += vmcb->control.next_rip - vmcb->save.rip;
}
For Page Faults (VMEXIT_NPF):
EXITINFO1 contains page fault error code:
EXITINFO2 contains the faulting guest physical address.
void handle_npf(struct vmcb *vmcb, uint64_t error, uint64_t gpa) {
bool write = (error >> 1) & 1;
bool user = (error >> 2) & 1;
/* Map the page in nested page tables */
paddr_t hpa = allocate_host_page();
map_npt_page(vmcb, gpa, hpa, write ? NPT_P | NPT_RW : NPT_P);
/* Guest will retry the access */
}
AMD-V includes a 'NRIP' (Next RIP) field in the VMCB that contains the RIP value after the intercepted instruction. This saves the hypervisor from having to decode instruction length. Not all exits populate NRIP—check the 'NRIP Save' bit in EXITINFO1 or consult AMD documentation for which exits support it.
The hypervisor often needs to deliver events (interrupts, exceptions) into the guest. AMD-V provides the EVENTINJ field in the VMCB control area for this purpose.
EVENTINJ Field Format:
Bits 7:0 - Vector (interrupt/exception number)
Bit 8-10 - Type:
0 = External interrupt
2 = NMI
3 = Exception (fault/trap)
4 = Software interrupt (INT n)
Bit 11 - Error code valid
Bit 31 - Valid (set to inject event)
Bits 63:32 - Error code (if bit 11 set)
Injecting a Timer Interrupt:
void inject_timer_interrupt(struct vmcb *vmcb) {
vmcb->control.eventinj =
0x20 | /* Vector 0x20 (timer) */
(0 << 8) | /* Type: external interrupt */
(0 << 11) | /* No error code */
(1ULL << 31); /* Valid */
}
Injecting a Page Fault:
void inject_page_fault(struct vmcb *vmcb, uint64_t cr2, uint32_t error) {
/* Set CR2 first */
vmcb->save.cr2 = cr2;
vmcb->control.eventinj =
14 | /* Vector 14 (#PF) */
(3 << 8) | /* Type: exception */
(1 << 11) | /* Error code valid */
(1ULL << 31) | /* Valid */
((uint64_t)error << 32); /* Error code */
}
Event Injection on VMRUN:
When VMRUN executes with a valid EVENTINJ:
V_INTR_MASKING:
AMD-V includes sophisticated virtual interrupt handling. The V_INTR_MASKING bit in VMCB controls whether the physical IF flag or a virtual IF flag (V_IF) controls interrupt masking:
/* Enable virtual interrupt masking */
vmcb->control.int_ctl |= V_INTR_MASKING;
/* Now guest CLI/STI affects V_IF instead of physical IF */
/* Physical interrupts can still be delivered to host */
This allows the hypervisor to receive physical interrupts even when the guest has disabled interrupts—essential for responsive hypervisor scheduling.
Virtual Interrupt Pending:
The V_IRQ bit and V_INTR_PRIO field allow hardware-assisted virtual interrupt delivery:
/* Signal pending virtual interrupt */
vmcb->control.int_ctl |= V_IRQ; /* Interrupt pending */
vmcb->control.int_ctl |= (8 << 16); /* Priority 8 */
vmcb->control.int_vector = 0x30; /* Vector 0x30 */
/* On VMRUN, if guest has interrupts enabled and priority allows,
the interrupt will be delivered automatically without exit */
With V_IRQ and AVIC (AMD Virtual Interrupt Controller), many interrupt deliveries happen without VM exits. The hypervisor sets up pending interrupts, and hardware delivers them transparently when the guest is ready. This dramatically improves interrupt-heavy workload performance.
While AMD-V and Intel VT-x solve the same problem, their implementations differ in ways that affect hypervisor design and performance. Understanding these differences is essential for cross-platform virtualization development.
| Aspect | AMD-V (SVM) | Intel VT-x (VMX) |
|---|---|---|
| State Structure | VMCB (direct memory access) | VMCS (requires VMREAD/VMWRITE) |
| Entry Instruction | Single VMRUN | VMLAUNCH (first) + VMRESUME (subsequent) |
| Host State Storage | Separate Host Save Area (HSA) | Within VMCS host-state area |
| Nested Paging | NPT (Nested Page Tables) - Introduced first (2007) | EPT (Extended Page Tables) - Intel's equivalent (2008) |
| TLB Tagging | ASID (12-bit Address Space ID) | VPID (16-bit Virtual Processor ID) |
| Virtual APIC | AVIC (AMD Virtual Interrupt Controller) | APICv (APIC Virtualization) |
| Instruction Decode | NRIP field often available | Instruction length provided on most exits |
| Clean Bits | VMCB Clean Bits for selective save/restore | VMCS shadowing for nested virt |
Writing Cross-Platform Hypervisors:
KVM, Xen, and other major hypervisors support both AMD-V and Intel VT-x. The typical approach:
struct vmm_ops {
int (*init_cpu)(void *vmm);
int (*create_vm)(struct vm **vm);
int (*create_vcpu)(struct vm *vm, struct vcpu **vcpu);
int (*run_vcpu)(struct vcpu *vcpu);
int (*handle_exit)(struct vcpu *vcpu);
int (*inject_event)(struct vcpu *vcpu, struct event *ev);
};
extern struct vmm_ops svm_ops; /* AMD implementation */
extern struct vmm_ops vmx_ops; /* Intel implementation */
struct vmm_ops *current_ops;
void init_virtualization(void) {
if (cpu_has_svm()) {
current_ops = &svm_ops;
svm_init();
} else if (cpu_has_vmx()) {
current_ops = &vmx_ops;
vmx_init();
} else {
panic("No hardware virtualization support");
}
}
Over time, AMD and Intel have adopted each other's best ideas. Intel added EPT after seeing NPT's benefits; AMD added AVIC after seeing APICv's advantages. Modern implementations are more similar than early versions, making cross-platform hypervisor development easier.
AMD-V (SVM) provides a complete hardware virtualization solution that enables efficient, secure isolation of virtual machines on AMD processors. Its straightforward VMCB-based programming model makes it accessible to hypervisor developers while delivering excellent performance.
What's Next:
In the next page, we'll explore Extended Page Tables (EPT) and Nested Page Tables (NPT)—the hardware memory virtualization features that eliminate shadow page tables and dramatically improve VM memory performance. Understanding nested paging is essential for understanding modern hypervisor memory management.
You now understand AMD-V's SVM architecture—from VMCB structure and intercept controls to VMRUN mechanics and exit handling. Combined with your knowledge of Intel VT-x, you can reason about hardware virtualization on both major x86 platforms.