기밀 컴퓨팅 (Confidential Computing)

사용 중인 데이터(data-in-use)를 하드웨어 기반으로 보호하는 기밀 컴퓨팅(Confidential Computing) 기술을 심층 분석합니다. AMD SEV/SEV-ES/SEV-SNP, Intel TDX, ARM CCA/Realm의 아키텍처 설계와 리눅스 커널 통합을 비교하고, 메모리 암호화(Encryption) 엔진, 원격 증명(Remote Attestation), SVSM, KVM fd-based private memory(gmem), virtio bounce buffer, 기밀 컨테이너(Kata/CoCo), GPU TEE까지 포괄합니다.

전제 조건: 가상화(Virtualization) 개요, 커널 보안 개요, 메모리 관리(Memory Management) 문서를 먼저 읽으면 이해가 수월합니다. 기밀 컴퓨팅은 가상화 확장(VMX/SVM), 메모리 암호화, 하드웨어 신뢰 루트 개념과 밀접하게 연결됩니다.
일상 비유: 기밀 컴퓨팅은 투명 금고 안의 작업실과 같습니다. 클라우드 공급자(건물 관리인)는 금고 안에서 무엇이 실행 중인지 볼 수 없습니다. 작업자(VM/컨테이너)만이 금고 안에 들어갈 수 있고, 작업 결과를 외부에 선택적으로 공유합니다. 원격 증명은 "이 금고가 진짜이고 열쇠가 바뀌지 않았음"을 제3자가 검증하는 봉인 인증서입니다.

핵심 요약

항목AMD SEV-SNPIntel TDXARM CCA
제조사AMD (EPYC 7003+)Intel (Sapphire Rapids+)ARM (Armv9-A+)
암호화 엔진SME/SEV AES-128-XEX → AES-256-XTSTME/MKTME AES-128/256-XTSMEC (구현체 종속)
격리(Isolation) 단위VM (ASID 기반)TD (Trust Domain, HKID)Realm (Realm ID)
무결성(Integrity) 보호RMP (Reverse Map Table)SEPT + MAC (메모리 무결성)GPT (Granule Protection Table)
증명VCEK/VLEK 기반 ReportTDREPORT → Quote (QE/QGS)CCA Attestation Token (CCA Platform)
권한 계층VMPL 0~3SEAM Module + TDRoot/Realm/Secure/Non-secure 4세계
Linux 지원v5.19+ (게스트), v6.2+ (호스트 KVM)v6.2+ (게스트), v6.7+ (호스트 KVM)v6.9+ (실험적)
라이브 마이그레이션SEV-SNP Migration AgentTDX Live Migration (v6.12+)미지원 (spec 진행 중)
  • 공통 목표 — 하이퍼바이저(Hypervisor)/호스트 OS를 TCB(Trusted Computing Base)에서 제외하여 "사용 중 데이터" 보호
  • 핵심 메커니즘 — 하드웨어 메모리 암호화 + 접근 제어(Access Control) + 원격 증명(Remote Attestation)
  • Linux 통합 — KVM의 fd-based private memory(gmem), SWIOTLB bounce buffer, guest_memfd
  • 에코시스템 — Confidential Containers(CoCo), SVSM, GPU TEE(NVIDIA CC, AMD SEV-TIO)
  • 데이터 보호 3종류 — 기밀 컴퓨팅은 "data-in-use"(사용 중) 보호에 집중합니다. 이는 기존의 "data-at-rest"(디스크 암호화)와 "data-in-transit"(TLS/IPsec)을 보완하여 데이터 수명주기 전체를 보호합니다
Confidential Computing Consortium (CCC): CCC는 Linux Foundation 산하의 프로젝트 그룹으로, AMD, Intel, ARM, Google, Microsoft, Red Hat 등이 참여합니다. 기밀 컴퓨팅의 표준화, 오픈소스 도구 개발, 교육을 주도합니다. 주요 프로젝트로는 Open Enclave SDK, Gramine, Enarx, coconut-svsm 등이 있습니다.

위협 모델

기밀 컴퓨팅의 위협 모델은 전통적 보안 모델과 근본적으로 다릅니다. 하이퍼바이저, 호스트 OS, 물리적 접근을 가진 관리자까지 비신뢰(untrusted)로 분류합니다. 이는 멀티테넌트 클라우드 환경에서 특히 중요합니다: 테넌트는 CSP의 인프라를 신뢰하지 않고도, 자신의 워크로드가 안전하게 실행되고 있음을 하드웨어 기반 증명으로 확인할 수 있습니다.

전통적 클라우드 보안에서는 CSP가 제공하는 소프트웨어 격리(가상화, 컨테이너)를 신뢰해야 합니다. 하지만 CSP의 관리자, 내부 공격자, 법적 강제(영장)에 의해 데이터가 노출될 위험이 있습니다. 기밀 컴퓨팅은 이 신뢰 관계를 하드웨어로 대체합니다.

TCB (Trusted Computing Base) CPU + 메모리 암호화 엔진 Guest VM 워크로드 Firmware PSP/TDX Module 비신뢰 영역 (Untrusted) 하이퍼바이저 KVM/QEMU 호스트 OS 커널 + 드라이버 물리 관리자 데이터센터 직원 외부 공격자 네트워크/DMA 원격 증명 (Remote Attestation) "이 VM이 기대한 코드/설정으로 실행 중임"을 제3자가 검증
TCB 최소화 원칙: 기밀 컴퓨팅은 TCB를 CPU 하드웨어 + 보안 프로세서 펌웨어(Firmware) + 게스트 코드로 한정합니다. 전통적 클라우드 모델에서는 하이퍼바이저, 호스트 커널, 관리 소프트웨어가 모두 TCB에 포함되어 공격 표면이 수십만 줄의 코드로 확대됩니다.

위협 모델 범위

위협보호 여부설명
하이퍼바이저의 메모리 읽기보호메모리 암호화로 평문 접근 차단
하이퍼바이저의 레지스터(Register) 읽기보호 (ES/SNP/TDX)VMCB/VMCS 상태 암호화
메모리 무결성 공격 (리플레이/스플라이싱)보호 (SNP/TDX)RMP/MAC 기반 무결성 검증
물리적 DRAM 프로빙보호Cold-boot 공격에도 암호화된 데이터만 노출
DMA 공격보호IOMMU + 메모리 암호화 결합
사이드 채널 공격부분적캐시(Cache) 타이밍, 전력 분석 등은 범위 외 (별도 완화 필요)
게스트 내부 취약점(Vulnerability)범위 외게스트 코드 자체의 버그는 기밀 컴퓨팅이 차단하지 않음
서비스 거부(DoS)범위 외하이퍼바이저는 VM을 종료할 수 있음

사이드 채널 공격과 기밀 컴퓨팅

기밀 컴퓨팅의 위협 모델에서 사이드 채널 공격은 "부분적 보호"로 분류됩니다. 일부 공격은 하드웨어 수준에서 완화되지만, 다른 공격은 소프트웨어 방어가 필요합니다.

사이드 채널CoCo 보호추가 완화
캐시 타이밍부분적 (AES 영향 없음)Constant-time 코드, CLFLUSH 제한
페이지 폴트(Page Fault) 채널SNP: RMP 보호, TDX: SEPT 보호ORAM (Oblivious RAM)
인터럽트(Interrupt) 타이밍Restricted Injection (TDX)인터럽트 배칭
전력 분석보호 안됨물리 보안, 노이즈 주입
Spectreretpoline/IBRS 적용 가능게스트 커널 Spectre 완화
CacheWarp (CVE-2023-20592)SEV-SNP 마이크로코드 패치(Patch)최신 펌웨어 업데이트
CacheWarp 취약점 (2023): SEV/SEV-ES VM에서 INVD 명령을 사용하여 캐시를 비우면, 이전 시점의 메모리 상태로 롤백(Rollback)할 수 있는 취약점이 발견되었습니다. SEV-SNP에서는 RMP의 무결성 검증으로 부분적으로 완화되며, AMD는 마이크로코드 업데이트를 통해 수정했습니다. 항상 최신 PSP 펌웨어와 마이크로코드를 유지하세요.

TCB 크기 비교

구성 요소코드 크기 (대략)TCB 포함?
CPU 하드웨어 (마이크로코드 포함)~수백 KB
PSP 펌웨어 (AMD) / TDX Module (Intel)~100~200 KB
게스트 펌웨어 (OVMF)~2~4 MB
게스트 커널 (Linux)~10~30 MB
하이퍼바이저 (KVM + QEMU)~10~15 MB아니오
호스트 OS~수십 MB아니오
관리 소프트웨어~수 GB아니오

메모리 암호화 엔진

기밀 컴퓨팅의 기반은 하드웨어 메모리 암호화입니다. CPU 내부의 메모리 컨트롤러에 AES 엔진이 통합되어, 모든 DRAM 트래픽을 실시간(Real-time)으로 암호화/복호화(Decryption)합니다. 이 암호화는 CPU 캐시(L1/L2/L3) 이후에 적용되므로, 캐시 내부의 데이터는 평문으로 유지되어 성능 영향이 최소화됩니다. 즉, CPU에서 메모리 컨트롤러로 나가는 시점에서만 AES 변환이 일어납니다.

CPU 코어 평문 데이터 L1/L2/L3 평문 캐시 메모리 컨트롤러 AES-XTS 엔진 128/256-bit 키, ASID/HKID 선택 DRAM 암호문 키 관리 유닛 PSP(SEV) / TDX Module / RMM 플랫폼별 암호화 방식 AMD SME/SEV: C-bit(47번) = 1 → 암호화, AES-128-XEX (Gen3) / AES-256-XTS (Gen4+) Intel TME/MKTME: KeyID(물리 주소 상위비트), AES-128/256-XTS, TDX는 HKID 자동 관리 ARM MEC: Memory Encryption Context, GPT와 연동, 구현체(SoC)별 알고리즘 선택

AMD SME (Secure Memory Encryption)

SME는 AMD EPYC 1세대(Naples)부터 도입된 투명 메모리 암호화입니다. 물리 주소(Physical Address)의 C-bit(bit 47)이 1이면 해당 페이지(Page)를 AES 엔진이 암호화합니다. SME는 SEV의 기반 기술이지만, 독립적으로도 사용 가능합니다: 비가상화 환경에서도 cold-boot 공격을 방어합니다.

모드암호화 범위키 관리설정
SME선택적 (C-bit 페이지)단일 플랫폼 키mem_encrypt=on
TSME전체 메모리 (투명)단일 키BIOS 설정
SEVVM별 (ASID 기반)VM당 고유 키KVM SEV API
/* arch/x86/mm/mem_encrypt_amd.c — SME 초기화 */
void __init sme_enable(struct boot_params *bp)
{
    unsigned long me_mask;

    /* CPUID Fn8000_001F[EAX] bit 0 = SME 지원 */
    if (!(cpuid_eax(0x8000001f) & BIT(0)))
        return;

    /* C-bit 위치 = CPUID Fn8000_001F[EBX] bits 5:0 */
    me_mask = 1UL << (cpuid_ebx(0x8000001f) & 0x3f);
    sme_me_mask = me_mask;

    /* MSR_AMD64_SYSCFG의 MEM_ENCRYPT 비트 활성화 */
    wrmsrl(MSR_AMD64_SYSCFG,
           rdmsrl(MSR_AMD64_SYSCFG) | MSR_AMD64_SYSCFG_MEM_ENCRYPT);
}

Intel TME/MKTME

TME(Total Memory Encryption)는 전체 메모리를 단일 키로 암호화하고, MKTME(Multi-Key TME)는 최대 64~256개의 키를 지원합니다. TDX에서는 HKID(Host Key ID)를 통해 TD별 키를 자동 관리합니다. TME와 MKTME는 독립적으로 사용할 수도 있지만, TDX는 MKTME를 필수로 활용합니다.

기능TMEMKTMETDX (MKTME 활용)
키 수1 (하드웨어 생성)최대 256키 풀을 HKID로 분배
키 관리자동 (부팅 시)OS/VMM 명시적TDX Module 관리
소프트웨어 접근불필요 (투명)PCONFIG + KeyIDSEAMCALL (VMM은 키 접근 불가)
무결성없음없음MAC 태그 포함
용도Cold-boot 방어멀티테넌트 격리기밀 VM 격리
/* arch/x86/virt/vmx/tdx/tdx.c — MKTME KeyID 범위 확인 */
static int __init tdx_init(void)
{
    u32 eax, ebx, ecx, edx;

    /* CPUID.19H — TME 관련 정보 */
    cpuid_count(0x1b, 0, &eax, &ebx, &ecx, &edx);

    /* TDX가 사용할 수 있는 private HKID 범위 */
    tdx_global_keyid = ecx;
    nr_tdx_keyids = edx;

    pr_info("TDX: %u private KeyIDs available\n", nr_tdx_keyids);
    return 0;
}
AES-XTS vs XEX: SEV 1세대(EPYC 7001)는 AES-128-XEX를 사용했으나, SEV-SNP(EPYC 7003+)부터 AES-256-XTS로 강화되었습니다. XTS는 블록 단위 암호화에서 동일 평문의 동일 암호문 생성을 방지하는 tweakable block cipher입니다.

암호화 키 관리 구조

키 유형플랫폼생성 위치수명노출 범위
Platform KeySEVPSP (부팅 시)전원 사이클PSP 내부
VM Encryption Key (VEK)SEV-SNPPSP (VM 시작 시)VM 수명AES 엔진 + PSP
VCEKSEV-SNP제조 시 fused영구PSP 서명 전용
HKID KeyTDXCPU (TD 생성 시)TD 수명AES 엔진
Sealing KeyTDXTDX ModuleTD 수명TD 내부
Realm KeyCCARMM/HWRealm 수명MEC 엔진

AES-XTS 동작 원리

/* AES-XTS 암호화 (개념 코드) */
/* 물리 주소를 tweak 값으로 사용하여 위치 종속 암호화 */
void aes_xts_encrypt_block(
    u8 *plaintext,       /* 16바이트 평문 블록 */
    u8 *ciphertext,      /* 16바이트 암호문 블록 */
    u8 *key1,            /* 데이터 암호화 키 */
    u8 *key2,            /* tweak 암호화 키 */
    u64 physical_address) /* tweak 입력: 물리 주소 */
{
    u8 tweak[16];

    /* 1단계: tweak 계산 — 물리 주소를 key2로 암호화 */
    aes_encrypt(key2, &physical_address, tweak);

    /* 2단계: 평문 XOR tweak */
    xor_block(plaintext, tweak, temp);

    /* 3단계: AES 암호화 */
    aes_encrypt(key1, temp, temp);

    /* 4단계: 암호문 XOR tweak */
    xor_block(temp, tweak, ciphertext);

    /* 결과: 같은 평문도 다른 물리 주소에서는 다른 암호문 생성 */
    /* → 데이터 이동/복제 공격 방어 */
}

AMD SEV 개요

AMD SEV(Secure Encrypted Virtualization)는 AMD-V 가상화 확장에 하드웨어 메모리 암호화를 결합한 기술입니다. 2016년 AMD EPYC 1세대(Naples)에서 처음 도입되었으며, 이후 ES(Encrypted State)와 SNP(Secure Nested Paging)를 거쳐 발전해왔습니다. PSP(Platform Security Processor, ARM Cortex-A5 기반)가 키 관리와 보안 정책을 담당합니다. SEV 시리즈는 현재 가장 성숙한 기밀 컴퓨팅 플랫폼으로, 주요 CSP(Azure, GCP, OCI)에서 프로덕션 서비스로 운영되고 있습니다.

세대프로세서기능보호 범위
SEVEPYC 7001 (Naples)VM별 메모리 암호화메모리 기밀성
SEV-ESEPYC 7002 (Rome)+ VMCB 레지스터 암호화+ 레지스터 기밀성
SEV-SNPEPYC 7003 (Milan)+ RMP 무결성 + 증명+ 메모리 무결성 + 원격 증명
SEV-SNP v2EPYC 9004 (Genoa)+ AES-256 + 성능개선+ 강화된 암호화

PSP (Platform Security Processor)

PSP(Platform Security Processor)는 AMD SoC에 통합된 ARM Cortex-A5 기반 보안 프로세서입니다. 메인 CPU와 물리적으로 분리된 독립 실행 환경에서 SEV 펌웨어를 실행하며, 키 생성/관리, 증명 보고서 서명, 게스트 시작/종료 프로토콜을 처리합니다. PSP는 SoC 부팅 시 가장 먼저 초기화되며, 메인 CPU보다 먼저 보안 상태를 설정합니다. Linux에서는 CCP(Cryptographic Co-Processor) 드라이버를 통해 PSP와 통신합니다.

/* drivers/crypto/ccp/sev-dev.c — PSP 명령 전송 */
static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
{
    struct psp_device *psp = psp_master;
    unsigned int reg;

    /* 명령 + 물리 주소를 PSP 메일박스에 기록 */
    iowrite32(__pa(data), psp->io_regs + psp->vdata->cmdbuff_addr_lo_reg);
    iowrite32(cmd, psp->io_regs + psp->vdata->cmdresp_reg);

    /* PSP 응답 대기 */
    wait_for_completion(&psp->sev_data->cmd_complete);

    reg = ioread32(psp->io_regs + psp->vdata->cmdresp_reg);
    if (psp_ret)
        *psp_ret = FIELD_GET(PSP_CMDRESP_STS, reg);

    return FIELD_GET(PSP_CMDRESP_STS, reg) ? -EIO : 0;
}

SEV 아키텍처 확장 (Zen 세대별)

Zen 세대코드명SEV 기능키 크기최대 ASID
Zen 1Naples (EPYC 7001)SEVAES-128-XEX15
Zen 2Rome (EPYC 7002)SEV, SEV-ESAES-128-XEX509
Zen 3Milan (EPYC 7003)SEV, SEV-ES, SEV-SNPAES-128-XEX509
Zen 4Genoa (EPYC 9004)SEV, SEV-ES, SEV-SNP v2AES-256-XTS1007
Zen 5Turin (EPYC 9005)SEV, SEV-ES, SEV-SNP v2+AES-256-XTS1007+

ASID 기반 키 격리

각 SEV VM에는 고유한 ASID(Address Space ID)가 할당되며, AES 엔진은 ASID를 참조하여 해당 VM의 암호화 키를 선택합니다. ASID 범위는 CPUID Fn8000_001F[ECX]로 확인합니다.

# SEV 지원 확인
dmesg | grep -i sev
# 출력 예: ccp 0000:22:00.1: sev enabled
#          SEV: 509 ASIDs, SEV-ES: 100 ASIDs

# SEV 펌웨어 버전 확인
cat /sys/devices/virtual/misc/sev/fw_version
# 출력 예: 1.55.24

# 사용 가능한 SEV ASID 수
cat /sys/devices/virtual/misc/sev/asids

SEV-ES (Encrypted State)

SEV-ES(Encrypted State)는 SEV에 게스트 레지스터 상태(VMCB Save Area) 암호화를 추가합니다. 기존 SEV에서는 VMEXIT 시 하이퍼바이저가 게스트의 RAX, RBX 등 범용 레지스터를 VMCB에서 읽을 수 있어, 민감한 데이터가 레지스터를 통해 노출될 위험이 있었습니다. SEV-ES는 VMCB Save Area를 VM의 암호화 키로 암호화하여 이 문제를 해결합니다. 대신 게스트와 하이퍼바이저 사이의 통신에 #VC(VMM Communication Exception) 핸들러(Handler)와 GHCB(Guest-Hypervisor Communication Block) 프로토콜이 필요합니다.

보호 대상SEVSEV-ES
게스트 메모리암호화암호화
범용 레지스터 (RAX, RBX, ...)노출됨암호화
제어 레지스터 (CR0, CR3, ...)노출됨암호화
디버그 레지스터 (DR0-DR7)노출됨암호화
FPU/SSE 상태노출됨암호화
VMCB 제어 영역VMM 관리VMM 관리 (일부만)
Guest (암호화된 상태) VMCB Save Area (암호화됨) GHCB (공유 페이지) #VC Exception Handler CPUID/IOIO/MSR → GHCB에 요청 기록 게스트 커널 (Linux) VMGEXIT Hypervisor (비신뢰) KVM / QEMU GHCB 읽기 → 에뮬레이션 → GHCB 응답 GHCB 프로토콜 1. 게스트: GHCB.sw_exit_code 설정 2. VMGEXIT 실행 3. VMM: 에뮬레이션 후 VMRUN VMGEXIT VMRUN

#VC 예외 처리 흐름

SEV-ES 게스트에서 CPUID, I/O 포트 접근, MSR 읽기/쓰기 등 하이퍼바이저 개입이 필요한 명령은 #VC(VMM Communication Exception, 벡터 29) 예외를 발생시킵니다. #VC는 기존 #GP나 #PF와 유사하게 IDT를 통해 전달됩니다. 게스트 커널의 #VC 핸들러가 GHCB(Guest-Hypervisor Communication Block)를 통해 요청/응답을 교환합니다.

#VC 핸들러의 역할은 "게스트가 하이퍼바이저에 최소한의 정보만 선택적으로 전달"하는 것입니다. 예를 들어, CPUID 요청 시 하이퍼바이저가 알아야 하는 정보(leaf 번호)만 GHCB에 기록하고, 게스트의 전체 레지스터 상태는 노출하지 않습니다.

/* arch/x86/coco/sev/vc.c — #VC 핸들러 핵심 */
static enum es_result vc_handle_exitcode(
    struct es_em_ctxt *ctxt,
    struct ghcb *ghcb,
    unsigned long exit_code)
{
    switch (exit_code) {
    case SVM_EXIT_CPUID:
        return vc_handle_cpuid(ghcb, ctxt);
    case SVM_EXIT_IOIO:
        return vc_handle_ioio(ghcb, ctxt);
    case SVM_EXIT_MSR:
        return vc_handle_msr(ghcb, ctxt);
    case SVM_EXIT_NPF:
        return vc_handle_mmio(ghcb, ctxt);
    default:
        return ES_UNSUPPORTED;
    }
}

/* GHCB를 통한 CPUID 에뮬레이션 */
static enum es_result vc_handle_cpuid(
    struct ghcb *ghcb,
    struct es_em_ctxt *ctxt)
{
    struct pt_regs *regs = ctxt->regs;

    ghcb_set_rax(ghcb, regs->ax);
    ghcb_set_rcx(ghcb, regs->cx);

    /* VMGEXIT로 하이퍼바이저에 요청 */
    return sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_CPUID, 0, 0);
}
#VC 핸들러 주의사항: #VC는 NMI-like 예외로 거의 모든 명령에서 발생할 수 있습니다. 핸들러 자체가 #VC를 유발하는 명령을 사용하면 무한 재귀가 발생하므로, 핸들러는 극도로 제한된 명령 집합만 사용해야 합니다. Linux 커널에서는 이를 vc_safe_* 래퍼로 관리합니다.

GHCB 프로토콜 v2

GHCB(Guest-Hypervisor Communication Block)는 2페이지(8KB) 크기의 공유 메모리 구조체(Struct)입니다. SEV-SNP에서는 GHCB 프로토콜 v2가 사용되며, NAE(Non-Automatic Exit) 이벤트 처리를 표준화합니다.

/* arch/x86/include/asm/sev-common.h — GHCB 구조체 */
struct ghcb {
    u8  reserved1[0xcb];
    u8  cpl;
    u8  reserved2[0x74];
    u64 rax;
    u8  reserved3[0x100];
    u64 rcx;
    u64 rdx;
    u64 rbx;
    u8  reserved4[0x70];
    u64 sw_exit_code;     /* NAE 이벤트 코드 */
    u64 sw_exit_info_1;   /* 이벤트 파라미터 1 */
    u64 sw_exit_info_2;   /* 이벤트 파라미터 2 */
    u64 sw_scratch;       /* 스크래치 버퍼 GPA */
    u8  reserved5[0x38];
    u64 valid_bitmap[2]; /* 유효 필드 비트맵 */
    u64 protocol_version; /* GHCB 프로토콜 버전 */
    u64 ghcb_usage;       /* GHCB 사용 유형 */
} __packed;

/* NAE 이벤트 코드 */
#define SVM_EXIT_CPUID    0x72
#define SVM_EXIT_IOIO     0x7b
#define SVM_EXIT_MSR      0x7c
#define SVM_EXIT_NPF      0x400
#define SVM_EXIT_AP_RESET_HOLD 0x80000004
#define SVM_EXIT_SNP_PSC       0x80000010 /* Page State Change */
#define SVM_EXIT_GUEST_REQUEST 0x80000011 /* 증명 요청 */

SEV-ES VMGEXIT 명령

VMGEXIT는 SEV-ES/SNP 게스트에서 사용하는 특수 VMEXIT 명령입니다. 일반 VM에서는 특정 명령(CPUID, RDMSR 등)이 자동으로 VMEXIT를 유발하지만, SEV-ES에서는 게스트가 GHCB를 설정한 후 명시적으로 VMGEXIT를 호출합니다. 이는 게스트가 어떤 정보를 하이퍼바이저에 전달할지 선택적으로 제어할 수 있음을 의미합니다.

/* VMGEXIT 실행 (어셈블리) */
/* SEV-ES/SNP에서는 "rep; vmmcall" = VMGEXIT */
static inline void sev_es_wr_ghcb_msr(u64 val)
{
    asm volatile("wrmsr"
        : : "c"(MSR_AMD64_SEV_ES_GHCB),
            "a"((u32)val),
            "d"((u32)(val >> 32)));
}

static inline void VMGEXIT(void)
{
    asm volatile("rep; vmmcall" ::: "memory");
}

SEV-ES 조기 부팅 (Early Boot)

SEV-ES 게스트의 부팅 초기 단계에서는 Linux 커널의 일반 예외 처리 인프라가 아직 초기화되지 않았습니다. 따라서 압축 해제 단계(decompressor)와 startup_64에서 별도의 #VC 핸들러가 필요합니다.

/* arch/x86/boot/compressed/sev.c — 조기 #VC 핸들러 */
void do_vc_no_ghcb(struct pt_regs *regs,
                    unsigned long exit_code)
{
    /* GHCB 초기화 전이므로 MSR 프로토콜 사용 */
    /* MSR 0xC001_0130(GHCB MSR)에 직접 요청/응답 */
    switch (exit_code) {
    case SVM_EXIT_CPUID: {
        u64 val;
        /* MSR 프로토콜: CPUID 요청 인코딩 */
        val = GHCB_CPUID_REQ_EAX |
              ((u64)regs->ax << 32);
        native_wrmsrl(MSR_AMD64_SEV_ES_GHCB, val);
        asm volatile("rep; vmmcall");
        val = native_rdmsrl(MSR_AMD64_SEV_ES_GHCB);
        regs->ax = val >> 32;
        break;
    }
    }
}

SEV-SNP

SEV-SNP(Secure Nested Paging)는 SEV-ES에 메모리 무결성 보호를 추가합니다. RMP(Reverse Map Table)가 모든 물리 페이지의 소유권을 추적하여, 하이퍼바이저의 메모리 리맵 공격(앨리어싱, 리플레이)을 차단합니다. SEV-SNP는 기밀 컴퓨팅의 "완성형"으로, 기밀성(SEV) + 레지스터 보호(ES) + 무결성(SNP) + 원격 증명을 모두 제공합니다.

왜 RMP가 필요한가: SEV/SEV-ES만으로는 하이퍼바이저가 NPT(Nested Page Table)를 조작하여 게스트의 GPA→HPA 매핑(Mapping)을 변경할 수 있습니다. 예를 들어, 게스트 코드 페이지(GPA 0x1000)를 악의적 코드가 있는 HPA로 리맵하거나, 두 GPA를 같은 HPA에 매핑(앨리어싱)하여 정보를 유출할 수 있습니다. RMP는 HPA → (ASID, GPA, VMPL) 역방향 매핑을 유지하여, NPT 워크 결과를 항상 RMP와 대조합니다.
RMP (Reverse Map Table) GPA ASID VMPL Assigned Immutable 0x1000 0 No ← HV 소유 0x2000 1 0 Yes No ← VM1 VMPL0 0x3000 1 2 Yes No ← VM1 VMPL2 0x4000 2 0 Yes No ← VM2 VMPL0 RMP 검증 흐름 (모든 메모리 접근) 1. NPT 워크 → HPA 획득 2. RMP[HPA] 조회 → ASID + VMPL 검증 3. 불일치 시 → #NPF(Nested Page Fault) 4. 통과 시 → AES 엔진에서 복호화 RMP가 차단하는 공격 1. 앨리어싱 공격 2개의 GPA → 같은 HPA 매핑 시도 RMP: GPA 불일치 → #NPF 발생 2. 리플레이 공격 이전 시점의 메모리를 복원 시도 RMP: 시퀀스 번호 불일치 → 차단 3. 크로스-VM 리맵 VM1의 페이지를 VM2에 매핑 시도 RMP: ASID 불일치 → #NPF 발생

VMPL (Virtual Machine Privilege Level)

SEV-SNP는 게스트 내부에 4단계 권한(VMPL 0~3)을 도입합니다. VMPL 0이 가장 높은 권한으로 SVSM이 실행되고, VMPL 2~3에서 게스트 OS가 실행됩니다.

/* arch/x86/include/asm/sev-common.h — VMPL 관련 정의 */
#define VMPL_0    0   /* 최고 권한 — SVSM 실행 */
#define VMPL_1    1   /* 예약 */
#define VMPL_2    2   /* 게스트 커널 */
#define VMPL_3    3   /* 게스트 유저스페이스 */

/* VMPL에 따른 RMP 권한 비트 */
struct rmp_entry {
    u64 gpa;
    u16 asid;
    u8  vmpl;
    u8  perms;     /* R/W/X 각 VMPL별 */
    u8  assigned : 1;
    u8  validated : 1;
    u8  immutable : 1;
};

페이지 검증 (Page Validation)

SEV-SNP 게스트는 PVALIDATE 명령으로 각 페이지를 명시적으로 검증(validate)해야 합니다. PVALIDATE는 RMP에 해당 페이지가 이 게스트의 소유임을 기록합니다. 검증되지 않은 페이지에 접근하면 #VC 예외가 발생합니다. 이 메커니즘은 하이퍼바이저가 게스트 모르게 새 페이지를 추가하는 공격을 방지합니다. 게스트가 명시적으로 검증하지 않은 페이지는 접근할 수 없으므로, 악의적 페이지 주입이 차단됩니다.

PVALIDATE와 RMPADJUST: PVALIDATE는 게스트가 페이지를 검증하는 명령이고, RMPADJUST는 게스트가 VMPL 권한을 조정하는 명령입니다. 두 명령 모두 VMPL 0에서만 실행 가능하며, SVSM이 활성화된 환경에서는 게스트 커널(VMPL 2)이 SVSM(VMPL 0)에 위임합니다.
/* arch/x86/kernel/sev.c — PVALIDATE 래퍼 */
static int pvalidate_pages(unsigned long vaddr,
                          unsigned int npages, bool validate)
{
    unsigned long va = vaddr;
    int i, rc;

    for (i = 0; i < npages; i++, va += PAGE_SIZE) {
        /* PVALIDATE: RMP에 이 페이지가 이 게스트 소유임을 기록 */
        rc = pvalidate(va, RMP_PG_SIZE_4K, validate);
        if (rc)
            return rc;
    }
    return 0;
}

SEV-SNP 증명

원격 증명(Remote Attestation)은 "이 VM이 기대한 코드와 설정으로 기밀 환경에서 실행 중임"을 제3자가 검증하는 프로토콜입니다. 기밀 컴퓨팅에서 증명은 신뢰의 기반입니다. 메모리 암호화만으로는 "이 VM이 정말 기밀 환경에서 실행 중인지" 확인할 수 없으므로, 하드웨어가 서명한 보고서를 통해 검증합니다. SEV-SNP는 PSP가 서명한 Attestation Report를 제공합니다.

증명의 핵심 질문: 원격 증명은 다음 4가지 질문에 답합니다:
  1. 플랫폼 정당성: 이 CPU가 진짜 AMD/Intel/ARM 기밀 컴퓨팅 하드웨어인가?
  2. 펌웨어 버전: PSP/TDX Module/RMM이 최신 보안 패치가 적용된 버전인가?
  3. 게스트 측정값: 게스트 펌웨어와 커널이 기대한 이미지로 부팅되었는가?
  4. 런타임 상태: 실행 중에 디버그 모드가 활성화되는 등의 정책 위반이 없었는가?
Guest VM MSG_REPORT_REQ + 64B user_data PSP (AMD SP) Report 생성 VCEK/VLEK 서명 검증자 (Verifier) Report 검증 인증서 체인 확인 SNP_GUEST_REQUEST Report Attestation Report 구조 (0x4A0 바이트) 측정값 MEASUREMENT (SHA-384, 48B) 정책 POLICY (8B) SMT/디버그/마이그레이션 플랫폼 정보 TCB VERSION CPU/SNP/마이크로코드 서명 ECDSA P-384 VCEK 또는 VLEK 인증서 체인: AMD Root Key (ARK) → AMD SEV Key (ASK) → VCEK/VLEK VCEK = 칩 고유 키 (HW fused) | VLEK = 벤더 발급 키 (CSP 운영)

증명 요청 코드

/* 사용자 공간에서 증명 보고서 요청 (snp-attestation-report) */
#include <linux/sev-guest.h>
#include <sys/ioctl.h>

int get_attestation_report(void) {
    int fd = open("/dev/sev-guest", O_RDWR);
    struct snp_guest_request_ioctl req = {};
    struct snp_report_req report_req = {};
    struct snp_report_resp report_resp = {};

    /* 64바이트 사용자 데이터 (nonce, challenge) */
    memcpy(report_req.user_data, nonce, 64);
    report_req.vmpl = 0;

    req.msg_version = 1;
    req.req_data = (__u64)&report_req;
    req.resp_data = (__u64)&report_resp;

    return ioctl(fd, SNP_GET_REPORT, &req);
}
VCEK vs VLEK: VCEK(Versioned Chip Endorsement Key)는 각 CPU 칩에 물리적으로 고정된(fused) 키로, AMD KDS(Key Distribution Service)에서 인증서를 다운로드합니다. VLEK(Versioned Loaded Endorsement Key)는 CSP가 AMD에서 발급받아 PSP에 주입하는 키로, 칩 고유 식별을 피하고 싶을 때 사용합니다.

SNP 증명 보고서 검증 예시

# snpguest CLI를 사용한 증명 보고서 획득 및 검증

# 1. 증명 보고서 요청 (게스트 내부)
snpguest report attestation.bin request.bin --random
# attestation.bin: 1184바이트 바이너리 보고서

# 2. 보고서 내용 확인
snpguest display report attestation.bin
# Version:       2
# Guest SVN:     1
# Policy:        0x30000 (SMT 허용, 디버그 금지)
# Measurement:   a1b2c3d4...  (SHA-384)
# Platform Info: SME | SEV | SEV-ES | SEV-SNP
# Signing Key:   VCEK

# 3. VCEK 인증서 다운로드 (AMD KDS)
snpguest fetch vcek der \
  --processor-model Milan \
  /tmp/certs/

# 4. 인증서 체인 확인
snpguest verify certs /tmp/certs/
# ARK → ASK → VCEK: OK

# 5. 보고서 서명 검증
snpguest verify attestation attestation.bin /tmp/certs/
# Attestation report signature verification: SUCCESS

증명 정책 필드 상세

Policy 비트필드설명
Bit 0NODBG1 = 디버깅(Debugging) 비허용 (프로덕션)
Bit 1NOKS1 = 키 공유 비허용
Bit 2ES_REQUIRED1 = SEV-ES 필수
Bit 3NOSEND1 = 마이그레이션 비허용
Bit 4DOMAIN1 = 도메인 잠금(Lock)
Bit 5SEV1 = SEV 활성화 VM
Bits 16-17SMTSMT(동시 멀티스레딩) 정책
Bits 31-16MIN_FW최소 펌웨어 버전 요구

SNP 게스트 초기화 흐름

SEV-SNP 게스트의 Linux 커널 초기화는 일반 VM과 다른 특수 경로를 거칩니다.

/* arch/x86/coco/sev/core.c — SNP 게스트 초기화 순서 */

/* 1. 조기 탐지 (startup_64 이전) */
/*    CPUID Fn8000_001F — SEV/ES/SNP 비트 확인 */
/*    MSR 0xC0010131 (SEV_STATUS) — 활성 기능 확인 */

/* 2. 초기화 1단계: setup_arch() */
void __init snp_init(void)
{
    /* CC 벤더 등록 */
    cc_vendor = CC_VENDOR_AMD;
    cc_set_mask(sme_me_mask);

    /* CPUID 테이블 초기화 (펌웨어가 전달한 CPUID 값 사용) */
    snp_cpuid_init();

    /* Secrets 페이지 매핑 (증명에 사용) */
    snp_map_secrets_page();
}

/* 3. 초기화 2단계: 메모리 accept */
void __init snp_accept_memory(phys_addr_t start, phys_addr_t end)
{
    unsigned long npages = (end - start) >> PAGE_SHIFT;

    /* 각 페이지를 PVALIDATE로 검증 */
    /* 또는 SVSM이 있으면 SVSM에 위임 */
    if (snp_vmpl)
        svsm_pvalidate_pages(start, npages, true);
    else
        pvalidate_pages(start, npages, true);
}

/* 4. 초기화 3단계: SWIOTLB bounce buffer 설정 */
/*    shared 영역 할당 + C-bit 클리어 */

SNP 보안 CPUID (Firmware-Validated CPUID)

SEV-SNP에서는 하이퍼바이저가 CPUID 결과를 조작할 수 있으므로, 펌웨어(OVMF)가 부팅 시 신뢰할 수 있는 CPUID 값을 CPUID 페이지에 기록합니다. 게스트 커널은 이 테이블을 참조하여 CPUID 위변조를 방지합니다.

/* arch/x86/coco/sev/core.c — 보안 CPUID 조회 */
static int snp_cpuid_calc(u32 func, u32 subfunc,
                          u32 *eax, u32 *ebx,
                          u32 *ecx, u32 *edx)
{
    const struct snp_cpuid_fn *fn;

    /* 펌웨어가 검증한 CPUID 테이블에서 조회 */
    fn = snp_cpuid_get_validated(func, subfunc);
    if (!fn)
        return -ENOENT;

    *eax = fn->eax;
    *ebx = fn->ebx;
    *ecx = fn->ecx_in == subfunc ? fn->ecx : fn->ecx_in;
    *edx = fn->edx;
    return 0;
}

Intel TDX 개요

Intel TDX(Trust Domain Extensions)는 기존 VMX(VT-x) 위에 SEAM(Secure Arbitration Mode)이라는 새로운 CPU 모드를 추가하여 Trust Domain(TD)을 생성합니다. TDX Module이라는 Intel 서명 펌웨어가 SEAM 모드에서 실행되며, TD의 메모리와 상태를 관리합니다. AMD SEV가 PSP라는 별도 하드웨어 보안 프로세서에 의존하는 반면, TDX는 메인 CPU의 새로운 동작 모드(SEAM)에서 소프트웨어(TDX Module)로 구현됩니다. 이 접근의 장점은 TDX Module을 소프트웨어 업데이트로 개선할 수 있다는 것입니다.

구성 요소역할실행 모드
TDX ModuleTD 생성/관리, 메모리 보호, 증명SEAM Root (Ring 0)
P-SEAMLDRTDX Module 로딩/업데이트SEAM (ACM)
TD (Trust Domain)기밀 VM 인스턴스SEAM Non-Root
VMM (KVM)자원 관리 (비신뢰)VMX Root
/* arch/x86/virt/vmx/tdx/tdx.c — TDX 모듈 초기화 */
static int __init init_tdx_module(void)
{
    int ret;

    /* 1단계: SEAMCALL[TDH.SYS.INIT] — TDX Module 초기화 */
    ret = seamcall(TDH_SYS_INIT, 0, 0, 0, 0, NULL);
    if (ret)
        return ret;

    /* 2단계: 모든 LP에서 TDH.SYS.LP.INIT 실행 */
    on_each_cpu(tdx_init_lp, NULL, 1);

    /* 3단계: TDX Module 상태를 SYS_READY로 전환 */
    ret = seamcall(TDH_SYS_CONFIG, 0, 0, 0, 0, NULL);

    pr_info("TDX module initialized successfully\n");
    return ret;
}
SEAMCALL vs TDCALL: SEAMCALL은 VMM(KVM)이 TDX Module의 기능을 호출할 때 사용하는 명령입니다(예: TD 생성, 메모리 추가). TDCALL은 TD 내부의 게스트가 TDX Module을 호출하는 명령입니다(예: 증명 보고서 요청, 페이지 공유 전환). 두 명령 모두 SEAM 모드로 진입합니다.

TDX Module 보안 속성

TDX Module은 기밀 컴퓨팅의 핵심 TCB 구성 요소입니다. 몇 가지 중요한 보안 속성을 갖고 있습니다.

속성설명
Intel 서명P-SEAMLDR이 로드 시 Intel 서명 검증(Signature Verification). 변조된 모듈 로드 불가
메모리 격리SEAMRR(SEAM Range Register)로 TDX Module 메모리를 HW 격리
단일 진입점(Entry Point)SEAMCALL/TDCALL을 통해서만 진입. 임의 코드 실행 불가
업데이트 가능Intel이 새 버전 배포 시 라이브 업데이트 가능 (TD 재시작(Reboot) 없이)
코드 크기~200KB 이하의 소형 TCB. 감사 가능한 코드 크기
상태 비저장모든 TD 상태는 TD 전용 메모리에 저장. Module 자체는 상태 없음
/* SEAMRR — TDX Module 메모리 보호 레지스터 */
/* IA32_SEAMRR_PHYS_BASE (MSR 0x1400) — 기본 주소 */
/* IA32_SEAMRR_PHYS_MASK (MSR 0x1401) — 마스크 + 활성화 */
/*
 * SEAMRR은 SMRR(System Management Range Register)과 유사하게
 * 물리 주소 범위를 하드웨어 수준에서 보호합니다.
 * Non-SEAM 모드에서의 접근은 무조건 abort됩니다.
 * 이 영역에 TDX Module 코드, 전역 메타데이터,
 * TD Control Structure(TDCS), TD Virtual Processor State(TDVPS)
 * 등이 저장됩니다.
 */

TDX 아키텍처

TDX의 메모리 격리는 Secure EPT(SEPT)와 MKTME 암호화의 조합으로 구현됩니다. 각 TD는 고유한 HKID(Host Key ID)를 부여받으며, TDX Module만이 SEPT를 관리할 수 있습니다. VMM(KVM)은 SEAMCALL을 통해 TDX Module에 메모리 추가/제거를 요청할 수 있지만, SEPT 자체를 직접 수정할 수 없습니다. 이는 VMM이 NPT를 자유롭게 수정할 수 있는 AMD SEV(non-SNP)와의 핵심적인 차이입니다. TDX에서는 하드웨어가 아닌 TDX Module(신뢰 소프트웨어)이 페이지 테이블(Page Table) 무결성을 보장합니다.

SEAM Mode (보안 중재) TDX Module Intel 서명 펌웨어 SEPT/TD 관리 P-SEAMLDR ACM 모듈 TDX Module 로더 VMX Root (비신뢰) KVM + QEMU SEAMCALL 호출 자원 할당만 가능 TD 1 (Trust Domain) 게스트 OS + 워크로드 TDCALL 호출 HKID = 3 TD 2 (Trust Domain) 게스트 OS + 워크로드 TDCALL 호출 HKID = 4 SEAMCALL TDCALL 메모리 보호 계층 Secure EPT (SEPT) TD별 페이지 테이블 TDX Module만 수정 가능 MKTME 암호화 HKID별 AES-256-XTS MAC 무결성 태그 포함 TD VMCS 암호화된 가상 머신 상태 VMM은 일부 필드만 읽기 가능

SEAMCALL 인터페이스

/* arch/x86/virt/vmx/tdx/tdx.c — 주요 SEAMCALL 목록 */

/* TD 생명주기 관리 */
#define TDH_MNG_CREATE       0x0009  /* TD Control Structure 생성 */
#define TDH_MNG_ADDCX       0x000A  /* TD에 제어 페이지 추가 */
#define TDH_MNG_INIT        0x0011  /* TD 초기화 완료 */
#define TDH_MNG_KEY_CONFIG  0x0008  /* HKID 할당 및 키 설정 */

/* 메모리 관리 */
#define TDH_MEM_PAGE_ADD    0x0D00  /* TD에 private 페이지 추가 */
#define TDH_MEM_SEPT_ADD    0x0C00  /* SEPT 페이지 추가 */
#define TDH_MEM_PAGE_AUG    0x0D40  /* 런타임 페이지 추가 (lazy) */

/* vCPU 관리 */
#define TDH_VP_CREATE       0x000E  /* TDVPS 생성 */
#define TDH_VP_ENTER        0x0010  /* TD vCPU 진입 */

/* arch/x86/kvm/vmx/tdx.c — KVM에서 TD 생성 */
static int tdx_vm_init(struct kvm *kvm)
{
    struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
    int ret;

    /* HKID 할당 */
    ret = tdx_keyid_alloc();
    if (ret < 0)
        return ret;
    kvm_tdx->hkid = ret;

    /* TDH.MNG.CREATE — TDCS 생성 */
    ret = seamcall(TDH_MNG_CREATE, kvm_tdx->tdcs_pa,
                   kvm_tdx->hkid, 0, 0, NULL);
    return ret;
}

TD Exit 처리

TDX에서 VM Exit(TD Exit)는 SEV-ES의 #VC와 유사하게 작동합니다. 게스트가 하이퍼바이저 개입이 필요한 명령을 실행하면, TDX Module이 제한적 정보만 VMM에 전달합니다.

/* arch/x86/coco/tdx/tdx.c — TDX 게스트 VE(Virtualization Exception) 핸들러 */
bool tdx_handle_virt_exception(
    struct pt_regs *regs,
    struct ve_info *ve)
{
    unsigned long val;
    bool ret = true;

    switch (ve->exit_reason) {
    case EXIT_REASON_HLT:
        /* TDCALL[TDG.VP.VMCALL] — VMM에 HLT 알림 */
        tdx_hlt();
        break;
    case EXIT_REASON_IO_INSTRUCTION:
        /* I/O 명령 에뮬레이션 */
        ret = tdx_handle_io(regs, ve);
        break;
    case EXIT_REASON_MSR_READ:
        /* TDVMCALL로 VMM에 MSR 읽기 요청 */
        ret = tdx_handle_msr(regs, ve);
        break;
    case EXIT_REASON_CPUID:
        /* TDCALL[TDG.VP.VMCALL] — CPUID 가상화 */
        ret = tdx_handle_cpuid(regs, ve);
        break;
    case EXIT_REASON_EPT_VIOLATION:
        /* MMIO 처리 */
        ret = tdx_handle_mmio(regs, ve);
        break;
    default:
        pr_warn("TDX: unexpected VE: %ld\n",
                ve->exit_reason);
        ret = false;
    }
    return ret;
}

/* TDVMCALL — 게스트 → VMM 통신 (TDX의 VMGEXIT 대응) */
static u64 __tdx_hypercall(struct tdx_module_args *args)
{
    /* TDCALL[TDG.VP.VMCALL] leaf */
    args->r10 = TDX_HYPERCALL_STANDARD;
    return __tdcall(TDG_VP_VMCALL, args);
}

Unaccepted Memory

TDX와 SEV-SNP 모두 게스트가 사용할 메모리를 명시적으로 "수락(accept)"해야 합니다. Linux v6.5+에서는 CONFIG_UNACCEPTED_MEMORY로 lazy accept를 지원하여, 부팅 시간을 대폭 단축합니다.

/* mm/page_alloc.c — 미수락 메모리 처리 */
static void accept_page(struct page *page, unsigned int order)
{
    phys_addr_t start = page_to_phys(page);
    phys_addr_t end = start + (PAGE_SIZE << order);

    /* 아키텍처별 accept 호출 */
    /* TDX: TDCALL[TDG.MEM.PAGE.ACCEPT] */
    /* SNP: PVALIDATE */
    arch_accept_memory(start, end);
}

/* 페이지 할당자에서 on-demand accept */
struct page *__alloc_pages(gfp_t gfp, unsigned int order,
                           int nid, nodemask_t *mask)
{
    struct page *page;

    page = get_page_from_freelist(...);
    if (page && page_is_unaccepted(page))
        accept_page(page, order);  /* 최초 사용 시 accept */
    return page;
}
Lazy Accept 효과: 256GB 메모리의 기밀 VM에서 전체 메모리를 부팅 시 accept하면 ~30초 소요됩니다. Lazy accept를 사용하면 부팅 시간이 ~2초로 단축되며, 나머지 페이지는 최초 사용 시 on-demand로 accept됩니다.

TDX 증명

TDX 증명은 2단계 프로토콜입니다. 1단계에서 TD 내부에서 TDCALL[TDG.MR.REPORT]로 TDREPORT를 생성합니다. TDREPORT는 로컬 검증용으로 MAC(HMAC-256)으로 보호되지만 원격 전송에는 적합하지 않습니다. 2단계에서 QE(Quoting Enclave, SGX 기반)가 TDREPORT를 ECDSA-P256으로 서명된 Quote로 변환하여 원격 검증자에게 전달합니다. TDX는 기존 SGX의 증명 인프라(DCAP, Intel PCS)를 재활용(Recycling)합니다.

TD Guest TDCALL [TDG.MR.REPORT] TDX Module TDREPORT 생성 MAC 서명 Quoting Enclave Quote 생성 ECDSA-P256 서명 검증 서비스 IAS/DCAP/MAA Quote 검증 TDREPORT 구조 (1024 바이트) REPORTMACSTRUCT CPUSVN, TEE_TCB_INFO MAC (HMAC-256) TD_INFO ATTRIBUTES, XFAM MRTD, MRCONFIGID REPORTDATA 64B 사용자 데이터 (nonce, 공개키 해시) 측정 레지스터 RTMR[0..3] (런타임 확장 가능) MRTD = TD 빌드 측정값 (SNP의 MEASUREMENT 대응) | RTMR = 런타임 측정 레지스터 (TPM PCR 유사) Quote 인증서 체인: Intel Root CA → PCK → Attestation Key → Quote 서명
/* TDX 게스트에서 TDREPORT 요청 */
#include <linux/tdx-guest.h>

int get_tdx_report(void) {
    int fd = open("/dev/tdx_guest", O_RDWR);
    struct tdx_report_req req = {};

    /* 64바이트 리포트 데이터 설정 */
    memcpy(req.reportdata, nonce, 64);

    /* TDCALL[TDG.MR.REPORT] 호출 */
    return ioctl(fd, TDX_CMD_GET_REPORT0, &req);
}

ARM CCA / Realm

ARM CCA(Confidential Compute Architecture)는 Armv9-A부터 도입된 RME(Realm Management Extension)를 기반으로 합니다. 기존 Normal/Secure 2개 세계에 RootRealm을 추가하여 4개 세계(World) 모델을 구현합니다. ARM의 접근은 x86과 달리 기존 TrustZone(Secure World)과 기밀 컴퓨팅(Realm World)을 완전히 분리하여, TrustZone의 TA가 기밀 VM 데이터에 접근할 수 없도록 합니다.

CCA와 TrustZone의 관계: ARM CCA는 기존 TrustZone을 대체하는 것이 아닙니다. TrustZone(Secure World)은 DRM, 결제, 생체인증 등 기존 용도로 계속 사용되며, CCA(Realm World)는 클라우드 기밀 컴퓨팅 전용입니다. 4개 세계가 동시에 존재하며, GPT가 각 세계의 메모리 접근을 하드웨어 수준에서 격리합니다. 자세한 내용은 TrustZone / OP-TEE 문서를 참고하세요.
Root World EL3 Monitor 세계 전환 관리 GPT 관리 페이지 소유권 설정 Secure World OP-TEE TrustZone OS Trusted Apps Realm World RMM (EL2) Realm Management Monitor Realm VM 기밀 워크로드 Normal World 하이퍼바이저 KVM (비신뢰) 일반 VM 비보호 GPT (Granule Protection Table) — 물리 페이지별 세계 소유권 각 4KB granule → Root | Secure | Realm | Non-secure 중 하나 지정 GPC (Granule Protection Check) — 하드웨어 접근 제어 Normal World → Realm 메모리 접근 시 → Granule Protection Fault

GPT (Granule Protection Table)

GPT는 물리 메모리(Physical Memory)의 각 granule(4KB)에 대해 소유 세계를 지정하는 2단계 계층 테이블입니다. EL3의 Monitor 펌웨어만이 GPT를 수정할 수 있으며, GPC(Granule Protection Check)가 모든 메모리 접근을 하드웨어 수준에서 검증합니다. GPT는 AMD의 RMP나 Intel의 SEPT와 유사한 역할을 하지만, ARM의 4세계 모델을 반영하여 더 세분화된 접근 제어를 제공합니다.

GPT 특성설명
구조L0(Block descriptor) + L1(Granule descriptor) 2단계
관리 주체EL3 Monitor (TF-A/BL31) 전용
검증 방식GPC (하드웨어 TLB 단계에서 수행)
위반 시Granule Protection Fault (GPF) 예외
캐시GPT 워크 결과가 TLB에 캐시됨 (TLBI PAALLOS로 무효화(Invalidation))
전환 비용GPT 엔트리 변경 + TLB 무효화 + 캐시 클린
/* GPT 엔트리 구조 (ARM RME spec) */
/* 각 granule에 대해 2비트 PAS(Physical Address Space) 지정 */
enum gpt_pas {
    GPT_PAS_ROOT     = 0b00,  /* EL3 전용 */
    GPT_PAS_SECURE   = 0b01,  /* Secure World */
    GPT_PAS_REALM    = 0b10,  /* Realm World (기밀 VM) */
    GPT_PAS_NONSEC   = 0b11,  /* Normal World */
};

/* GPT 전환: Normal → Realm (EL3 Monitor 호출) */
/* SMC를 통해 EL3에서 실행됨 */
int gpt_transition_pas(unsigned long pa,
                       enum gpt_pas from,
                       enum gpt_pas to)
{
    /* GPT L1 엔트리 수정 */
    gpt_set_entry(pa, to);
    /* TLB 무효화 (TLBI PAALLOS) */
    tlbi_paallos();
    return 0;
}

Realm 관리

RMM(Realm Management Monitor)은 Realm World의 EL2에서 실행되며, Realm VM의 생명주기를 관리합니다. 하이퍼바이저(Normal World EL2)와 분리된 독립 소프트웨어입니다. 참조 구현체인 TF-RMM(Trusted Firmware RMM)은 ARM이 오픈소스로 제공합니다. RMM의 역할은 AMD SEV-SNP에서 PSP + RMP가 하는 역할, TDX에서 TDX Module이 하는 역할과 유사합니다.

RMM 기능대응 (SEV-SNP)대응 (TDX)
Realm 생성/삭제PSP LAUNCH_START/FINISHSEAMCALL TDH.MNG.CREATE
메모리 보호 (RTT)RMPSEPT
vCPU 관리 (REC)VMCB + VMPLTDVPS
증명 토큰 생성PSP Attestation ReportTDREPORT + QE Quote
메모리 상태 전환PVALIDATE + PSCPAGE.ACCEPT + MapGPA
NEW BUILDING ACTIVE DESTROYED RMI_REALM_CREATE RMI_REALM_ACTIVATE RMI_REALM_DESTROY Realm 생성 상세 흐름 1. 하이퍼바이저 요청 SMC → EL3 Monitor → RMI_REALM_CREATE → Realm Descriptor 할당 2. 메모리 구성 RMI_DATA_CREATE (페이지 추가) GPT: Non-secure → Realm REC (vCPU 구조체) 생성 3. 활성화 및 실행 RMI_REALM_ACTIVATE → 측정값 봉인 RMI_REC_ENTER → Realm 진입 RSI_* → 게스트의 RMM 호출 Realm 측정 레지스터 RIM (Realm Initial Measurement): Realm 생성 시 측정값 (빌드 타임) REM[0..3] (Realm Extensible Measurement): 런타임 확장 가능 레지스터 (게스트가 RSI로 확장)

RMI / RSI 인터페이스

인터페이스호출자대상주요 명령
RMI (Realm Mgmt Interface)Normal World (KVM)RMMREALM_CREATE, DATA_CREATE, REC_ENTER
RSI (Realm Service Interface)Realm 게스트RMMATTESTATION_TOKEN, IPA_STATE_SET
PSCI모든 WorldEL3CPU_ON, CPU_OFF, SYSTEM_RESET

Realm Translation Table (RTT)

Realm의 2단계 주소 변환(Address Translation)은 RTT(Realm Translation Table)를 통해 수행됩니다. RTT는 일반 Stage 2 페이지 테이블과 구조적으로 유사하지만, RMM만이 관리할 수 있습니다. 하이퍼바이저는 RMI를 통해 RTT 변경을 요청하고, RMM이 검증 후 적용합니다.

/* RTT 관리 RMI 명령 */
#define RMI_RTT_CREATE      0xC400_0171  /* RTT 레벨 생성 */
#define RMI_RTT_DESTROY     0xC400_0172  /* RTT 레벨 파괴 */
#define RMI_RTT_MAP         0xC400_0173  /* Non-secure IPA 매핑 */
#define RMI_RTT_UNMAP       0xC400_0174  /* IPA 매핑 해제 */
#define RMI_RTT_READ_ENTRY  0xC400_0175  /* RTT 엔트리 읽기 */
#define RMI_DATA_CREATE     0xC400_0153  /* Realm 데이터 페이지 생성 */
#define RMI_DATA_DESTROY    0xC400_0154  /* Realm 데이터 페이지 파괴 */

/* REC(Realm Execution Context) = vCPU 구조체 */
#define RMI_REC_CREATE      0xC400_015A  /* vCPU 생성 */
#define RMI_REC_DESTROY     0xC400_015B  /* vCPU 파괴 */
#define RMI_REC_ENTER       0xC400_015C  /* Realm 진입 (vCPU 실행) */

ARM CCA Linux KVM 통합

ARM CCA의 Linux KVM 지원은 v6.9+에서 실험적으로 진행 중입니다. KVM이 Normal World EL2에서 실행되며, Realm 생성 시 SMC를 통해 RMM과 통신합니다.

/* arch/arm64/kvm/rme.c — Realm VM 생성 */
int kvm_realm_create(struct kvm *kvm)
{
    struct realm *realm = &kvm->arch.realm;
    int ret;

    /* RD(Realm Descriptor) 페이지 할당 */
    realm->rd = alloc_delegated_page();

    /* SMC: RMI_REALM_CREATE */
    ret = rmi_realm_create(
        virt_to_phys(realm->rd),
        &realm->params);

    if (ret)
        return -ENXIO;

    /* RTT(Realm Translation Table) 초기화 */
    ret = realm_create_rtt_levels(kvm);

    return ret;
}

/* Realm 메모리 추가 */
int realm_map_memory(struct kvm *kvm,
                     gpa_t gpa, struct page *page)
{
    /* 1. GPT 전환: Non-secure → Realm */
    rmi_granule_delegate(page_to_phys(page));

    /* 2. Realm에 데이터 페이지 추가 */
    rmi_data_create(
        virt_to_phys(realm->rd),
        page_to_phys(page),
        gpa, RMI_MEASURE);

    return 0;
}

CCA 증명 토큰

ARM CCA의 증명은 RSI(Realm Service Interface)를 통해 Realm 내부에서 요청합니다. RMM이 CCA Platform Token을 생성하며, PSA(Platform Security Architecture) Attestation Token 형식(CBOR/COSE)을 따릅니다.

/* RSI를 통한 CCA 증명 토큰 요청 (개념 코드) */
int realm_get_attestation_token(
    void *challenge, size_t challenge_len,
    void *token_buf, size_t *token_len)
{
    struct arm_smccc_res res;

    /* RSI_ATTESTATION_TOKEN_INIT */
    arm_smccc_smc(RSI_ATTESTATION_TOKEN_INIT,
        virt_to_phys(challenge), challenge_len,
        0, 0, 0, 0, 0, &res);

    /* RSI_ATTESTATION_TOKEN_CONTINUE (반복) */
    do {
        arm_smccc_smc(RSI_ATTESTATION_TOKEN_CONTINUE,
            virt_to_phys(token_buf), PAGE_SIZE,
            0, 0, 0, 0, 0, &res);
        *token_len += res.a1;
    } while (res.a0 == RSI_INCOMPLETE);

    return (res.a0 == RSI_SUCCESS) ? 0 : -EIO;
}

SVSM (Secure VM Service Module)

SVSM(Secure VM Service Module)은 SEV-SNP의 VMPL 0에서 실행되는 보안 서비스 모듈입니다. 게스트 OS(VMPL 2)에게 vTPM, VTOM, 시크릿 주입 등의 보안 서비스를 제공합니다. TCB를 최소화하면서 TPM 기능을 기밀 VM에 제공하는 핵심 컴포넌트입니다.

왜 SVSM이 필요한가: 기밀 VM에서는 호스트의 물리 TPM을 신뢰할 수 없습니다(호스트가 비신뢰이므로). 게스트 커널에 소프트웨어 TPM을 넣으면 커널 전체가 TCB에 포함되어 복잡성이 증가합니다. SVSM은 게스트 커널보다 높은 권한(VMPL 0)에서 최소한의 코드(~30KB)로 vTPM을 제공하여, 게스트 커널 자체의 무결성까지 vTPM으로 측정할 수 있게 합니다. 이는 물리 서버에서 TPM이 OS보다 먼저 초기화되는 것과 동일한 원리입니다.
SEV-SNP 기밀 VM VMPL 0 — SVSM (coconut-svsm) vTPM 2.0 PCR, 시크릿 봉인 VTOM 공유/개인 전환 Secret Injection 디스크 키, TLS 키 Live Migration 상태 전송 보호 VMPL 2 — 게스트 커널 (Linux) SVSM 호출 프로토콜: VMGEXIT → VMPL 0 전환 → 서비스 실행 → VMPL 2 복귀 게스트 커널은 SVSM을 통해 vTPM PCR 확장, 시크릿 획득, 페이지 검증 위임 VMPL 3 — 게스트 유저스페이스 애플리케이션 워크로드 SVSM Call Protocol

coconut-svsm 프로젝트

coconut-svsm은 AMD 주도의 오픈소스 SVSM 구현체입니다(Rust 기반). ~30,000줄의 소형 TCB로, 리눅스 커널 v6.7+에서는 SVSM 인식 코드가 포함되어 있습니다. Rust의 메모리 안전성 보장으로 VMPL 0에서 실행되는 코드의 취약점을 최소화합니다.

# coconut-svsm 빌드 (Rust nightly 필요)
git clone https://github.com/coconut-svsm/svsm.git
cd svsm

# Rust 도구체인 설치
rustup toolchain install nightly
rustup target add x86_64-unknown-none

# SVSM 빌드
make FEATURES="vtpm,default-vtpm"

# 결과물: svsm.bin (VMPL 0에 로드될 바이너리)
ls -la bin/svsm.bin

# QEMU에서 SVSM과 함께 SNP VM 실행
qemu-system-x86_64 \
  -enable-kvm \
  -cpu EPYC-v4 \
  -machine q35,confidential-guest-support=sev0 \
  -object sev-snp-guest,id=sev0,cbitpos=51,\
reduced-phys-bits=1,policy=0x30000,\
svsm=bin/svsm.bin \
  ...
/* arch/x86/coco/sev/svsm.c — SVSM 호출 래퍼 */
static int svsm_perform_call_protocol(
    struct svsm_call *call)
{
    /* SVSM Calling Area에 요청 기록 */
    call->caa->call_pending = 1;
    call->caa->rax = call->rax;

    /* VMGEXIT → PSP가 VMPL 0(SVSM)으로 전환 */
    asm volatile("rep; vmmcall");

    /* SVSM 처리 완료 후 VMPL 2로 복귀 */
    return call->caa->rax;
}

/* SVSM을 통한 PVALIDATE 위임 */
int svsm_pvalidate_pages(unsigned long vaddr,
                        unsigned int npages, bool validate)
{
    struct svsm_call call = {};

    call.rax = SVSM_CORE_PVALIDATE;
    call.rcx = vaddr;
    call.rdx = npages | (validate ? SVSM_PVALIDATE_FLAG_VALIDATE : 0);

    return svsm_perform_call_protocol(&call);
}
SVSM과 TDX의 대응: SEV-SNP의 SVSM(VMPL 0)은 TDX 환경에서 Intel Trust Authority 또는 TD-Shim이 유사한 역할을 합니다. ARM CCA에서는 RMM 자체가 이 역할의 일부를 수행합니다.

SVSM vTPM 상세

SVSM의 가장 중요한 서비스는 vTPM 2.0입니다. 물리 TPM 없이도 기밀 VM에 TPM 기능을 제공하여, 측정 부팅(Measured Boot), BitLocker/dm-crypt 키 봉인, IMA(Integrity Measurement Architecture)를 사용할 수 있게 합니다.

ℹ️

vTPM: swtpm 에뮬레이터, QEMU/libvirt vTPM 연동, SVSM vTPM 아키텍처, vTPM 라이브 마이그레이션, vtpm_proxy 커널 모듈(Kernel Module) 등의 상세 내용은 TPM 2.0 문서를 참조하세요.

/* SVSM vTPM 아키텍처 (개념 코드) */

/* VMPL 0 (SVSM) 내부 vTPM 구현 */
struct svsm_vtpm {
    u8  pcr[24][48];   /* 24개 PCR, SHA-384 */
    u8  ek_seed[32];    /* Endorsement Key 시드 */
    u8  srk[32];         /* Storage Root Key */
    bool locality_locked; /* TPM locality 잠금 */
};

/* PCR 확장 (게스트 커널 → SVSM 호출) */
int svsm_vtpm_pcr_extend(
    u32 pcr_idx,
    u16 hash_alg,
    const u8 *digest, size_t digest_len)
{
    struct svsm_call call = {
        .rax = SVSM_VTPM_CMD,
        .rcx = VTPM_CC_PCR_EXTEND,
        .rdx = pcr_idx,
    };

    /* digest를 SVSM 공유 버퍼에 복사 */
    memcpy(call.buffer, digest, digest_len);

    /* VMPL 0으로 전환하여 PCR 확장 */
    return svsm_perform_call_protocol(&call);
}

/* 시크릿 봉인 (vTPM seal) */
/* 특정 PCR 상태에서만 복호화 가능한 데이터 생성 */
/* → dm-crypt 키 보호, 설정 파일 봉인 */

SVSM 보안 서비스 전체 목록

서비스 ID기능용도
SVSM_CORE_PVALIDATEPVALIDATE 위임페이지 검증을 VMPL 0에서 수행
SVSM_CORE_CREATE_VCPUvCPU 생성 위임AP 부팅 시 VMPL 0에서 초기화
SVSM_VTPM_CMDvTPM 2.0 명령PCR 확장, 봉인/해제, 키 생성
SVSM_VTOMVTOM 관리Virtual Top-of-Memory 설정
SVSM_SECRET_INJECT시크릿 주입디스크 암호화 키, TLS 인증서
SVSM_MIGRATION마이그레이션 보조상태 직렬화(Serialization)/역직렬화

공유/개인 메모리 모델

기밀 VM은 메모리를 private(암호화, 호스트 접근 불가)과 shared(비암호화, 호스트와 공유)로 구분합니다. I/O와 virtio 통신은 shared 메모리를 통해 이루어집니다. 이 이분법은 기밀 컴퓨팅의 근본적 설계 결정입니다: 게스트 데이터는 보호하되, 하이퍼바이저와의 통신은 허용해야 하기 때문입니다.

Private/Shared 비율: 일반적인 기밀 VM에서 99%+ 메모리가 private이고, shared는 SWIOTLB bounce buffer(64~256MB), virtio vring, GHCB/통신 페이지 등 최소한만 사용합니다. shared 영역에 민감한 데이터가 노출되지 않도록 게스트 커널이 자동으로 관리합니다.
플랫폼private 표시shared 표시전환 메커니즘
SEV/SEV-ESC-bit = 1C-bit = 0페이지 테이블 C-bit 변경
SEV-SNPC-bit = 1 + RMP validatedC-bit = 0 + RMP sharedPVALIDATE + PSC(Page State Change)
TDXS-bit = 0 (Shared bit 미설정)S-bit = 1 (Shared bit 설정)TDCALL[TDG.MEM.PAGE.ACCEPT] + MapGPA
ARM CCARealm PASNon-secure PASRSI_IPA_STATE_SET + GPT 전환
/* arch/x86/coco/core.c — 통합 메모리 상태 전환 API */
int set_memory_encrypted(unsigned long addr, int numpages)
{
    switch (cc_vendor) {
    case CC_VENDOR_AMD:
        /* C-bit 설정 + PVALIDATE */
        return __set_memory_enc_pgtable(addr, numpages, true);
    case CC_VENDOR_INTEL:
        /* S-bit 클리어 + MapGPA(private) + PAGE.ACCEPT */
        return tdx_set_memory_private(addr, numpages);
    default:
        return 0;
    }
}

int set_memory_decrypted(unsigned long addr, int numpages)
{
    switch (cc_vendor) {
    case CC_VENDOR_AMD:
        /* C-bit 클리어 → shared로 전환 */
        return __set_memory_enc_pgtable(addr, numpages, false);
    case CC_VENDOR_INTEL:
        /* S-bit 설정 + MapGPA(shared) */
        return tdx_set_memory_shared(addr, numpages);
    default:
        return 0;
    }
}
Private → Shared 전환 시 주의: private 메모리를 shared로 전환할 때 반드시 해당 페이지의 내용을 먼저 제거(scrub)해야 합니다. 그렇지 않으면 암호화된 데이터가 평문으로 하이퍼바이저에 노출될 수 있습니다. 커널의 set_memory_decrypted()는 이 과정을 자동으로 처리합니다.

SEV-SNP Page State Change (PSC)

SEV-SNP에서 private↔shared 전환은 PSC(Page State Change) 프로토콜을 통해 수행됩니다. 게스트가 GHCB를 통해 VMM에 상태 변경을 요청하고, VMM이 RMP 업데이트를 처리합니다.

/* arch/x86/coco/sev/core.c — PSC 요청 */
static int snp_set_page_state(
    unsigned long paddr,
    unsigned int npages,
    int op)  /* SNP_PAGE_STATE_PRIVATE or SHARED */
{
    struct ghcb *ghcb;
    struct psc_entry *entries;
    int i, ret;

    ghcb = __sev_get_ghcb(&state);

    /* PSC 엔트리 배열 구성 */
    for (i = 0; i < npages; i++) {
        entries[i].gfn = (paddr >> PAGE_SHIFT) + i;
        entries[i].operation = op;
        entries[i].pagesize = RMP_PG_SIZE_4K;
    }

    /* GHCB NAE: SVM_EXIT_SNP_PSC */
    ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_PSC);
    ghcb_set_sw_exit_info_2(ghcb, __pa(entries));

    /* VMGEXIT → VMM이 RMP 업데이트 */
    sev_es_wr_ghcb_msr(__pa(ghcb));
    VMGEXIT();

    /* 결과 확인 */
    ret = ghcb->save.sw_exit_info_2;
    __sev_put_ghcb(&state);
    return ret;
}

/* 호스트 KVM 측 PSC 처리 */
/* arch/x86/kvm/svm/sev.c */
static int snp_handle_page_state_change(
    struct kvm_vcpu *vcpu)
{
    struct psc_entry *entry;

    for (i = 0; i < count; i++) {
        entry = &entries[i];
        if (entry->operation == SNP_PAGE_STATE_PRIVATE) {
            /* RMPUPDATE: Shared → Private */
            rmpupdate(entry->gfn << PAGE_SHIFT,
                      vcpu->kvm->arch.sev_info.asid,
                      RMP_ASSIGNED);
        } else {
            /* RMPUPDATE: Private → Shared */
            rmpupdate(entry->gfn << PAGE_SHIFT,
                      0, 0);
        }
    }
    return 0;
}

TDX MapGPA 프로토콜

TDX에서는 TDCALL[TDG.VP.VMCALL]의 MapGPA 서브콜을 통해 shared↔private 전환을 수행합니다.

/* arch/x86/coco/tdx/tdx.c — MapGPA로 메모리 상태 전환 */
static int tdx_map_gpa(phys_addr_t start,
                       phys_addr_t end, bool map_private)
{
    struct tdx_module_args args = {};
    u64 ret;

    /* shared GPA = GPA | shared_bit */
    /* private GPA = GPA (shared_bit 미설정) */
    args.r11 = TDVMCALL_MAP_GPA;
    args.r12 = map_private ? start : (start | tdx_shared_mask());
    args.r13 = end - start;

    ret = __tdx_hypercall(&args);
    if (ret)
        pr_err("TDX: MapGPA failed: %lld\n", ret);

    /* private으로 전환 시 PAGE.ACCEPT 필요 */
    if (map_private)
        tdx_accept_pages(start, end);

    return ret ? -EIO : 0;
}

게스트 펌웨어

기밀 VM의 초기 부팅 코드는 TCB에 포함되므로, 전용 펌웨어가 필요합니다. 일반 OVMF를 사용하면 #VC 핸들러, 메모리 검증, 증명 초기화 코드가 없어 부팅에 실패합니다. OVMF(Open Virtual Machine Firmware)의 기밀 컴퓨팅 전용 빌드가 표준이며, 대안으로 TCB를 더 줄인 TD-Shim(TDX)이나 Direct Boot(커널 직접 부팅)도 사용됩니다.

펌웨어 TCB 최소화: OVMF는 ~2~4MB의 코드로 TCB가 상당히 큽니다. 이를 줄이기 위해 TDX 전용 TD-Shim(Rust 기반, ~100KB)이나 Direct Boot(커널을 측정 후 직접 로드)가 대안으로 개발되고 있습니다. IGVM(Independent Guest VM) 포맷은 펌웨어+커널+initrd를 하나의 측정 가능한 이미지로 패키징합니다.
LAUNCH 호스트가 VM 생성 + 펌웨어 주입 MEASURE 펌웨어 이미지 SHA-384 측정 VERIFY 증명 보고서 측정값 포함 BOOT OVMF → GRUB → Linux 커널 RUNTIME 워크로드 실행 기밀 VM 전용 펌웨어 OVMF (SEV/TDX) edk2/OvmfPkg/AmdSev edk2/OvmfPkg/IntelTdx 메모리 검증, #VC 조기 핸들러 IGVM (VM 이미지 포맷) Independent Guest VM Format 펌웨어+커널+initrd 단일 이미지 측정 가능한 구조화 포맷 TD-Shim (TDX) 경량 TDX 전용 펌웨어 OVMF 대비 TCB 축소 Rust 기반 구현 초기 측정값 체인 펌웨어 이미지 → SHA-384 → MEASUREMENT(SEV) / MRTD(TDX) / RIM(CCA) → 증명 보고서에 포함
# SEV용 OVMF 빌드
git clone https://github.com/tianocore/edk2.git
cd edk2 && git submodule update --init
source edksetup.sh
build -a X64 -t GCC5 -p OvmfPkg/AmdSev/AmdSevX64.dsc -b RELEASE

# TDX용 OVMF 빌드
build -a X64 -t GCC5 -p OvmfPkg/IntelTdx/IntelTdxX64.dsc -b RELEASE

KVM 통합

리눅스 KVM은 기밀 컴퓨팅 VM을 관리하기 위해 fd-based private memory(gmem, guest_memfd) 아키텍처를 도입했습니다. 이 설계는 기밀 컴퓨팅의 가장 근본적인 요구사항을 해결합니다: "호스트가 게스트 메모리에 접근할 수 없어야 한다." 전통적 KVM에서는 호스트가 게스트 메모리를 mmap()으로 매핑하여 QEMU 프로세스(Process)가 직접 읽고 쓸 수 있었지만, 기밀 VM에서는 이것이 보안 위반입니다. guest_memfd는 물리 페이지를 할당하되 호스트 주소 공간(Address Space)에 매핑하지 않는 새로운 메모리 관리 방식입니다.

guest_memfd 도입 배경: 초기 KVM CoCo 지원에서는 기존 memslot 구조를 재활용하면서 하드웨어(RMP/SEPT)에만 의존하여 접근을 차단했습니다. 하지만 이 방식은 호스트 커널의 다양한 경로(page reclaim, KSM, swap 등)에서 게스트 페이지에 접근을 시도할 수 있어 복잡한 예외 처리가 필요했습니다. guest_memfd는 근본적으로 이 문제를 해결합니다.
전통 KVM (비기밀) QEMU 프로세스 mmap() 게스트 메모리 Guest VM 동일 HPA 공유 호스트 = 게스트 메모리 접근 가능 동일 물리 페이지를 호스트와 게스트가 모두 매핑 CoCo KVM (기밀) QEMU 프로세스 shared 만 접근 기밀 VM private 전용 접근 guest_memfd (fd-based private memory) KVM_CREATE_GUEST_MEMFD ioctl 호스트 프로세스 주소 공간에 매핑 불가 HPA 할당만 KVM이 관리, 접근은 게스트만 Private 메모리 guest_memfd (암호화) Shared 메모리 userspace mmap (I/O) 호스트 커널도 private 메모리 접근 불가 하드웨어(RMP/SEPT/GPT)가 접근 차단 → #NPF/#PF/GPF

KVM API 확장

/* include/uapi/linux/kvm.h — 기밀 VM 관련 API */

/* 기밀 VM 유형 */
#define KVM_X86_SEV_VM         2
#define KVM_X86_SEV_ES_VM      3
#define KVM_X86_SNP_VM         4
#define KVM_X86_TDX_VM         5

/* guest_memfd 생성 */
struct kvm_create_guest_memfd {
    __u64 size;       /* private 메모리 크기 */
    __u64 flags;      /* 예약 */
    __u64 reserved[6];
};

/* 사용 예: SEV-SNP VM 생성 */
int create_snp_vm(void)
{
    int kvm_fd = open("/dev/kvm", O_RDWR);
    int vm_fd;
    struct kvm_create_guest_memfd gmem = {
        .size = 256 * 1024 * 1024,  /* 256 MB */
    };

    /* SNP VM 생성 */
    vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, KVM_X86_SNP_VM);

    /* private 메모리 할당 */
    int gmem_fd = ioctl(vm_fd, KVM_CREATE_GUEST_MEMFD, &gmem);

    /* memory slot에 guest_memfd 연결 */
    struct kvm_userspace_memory_region2 region = {
        .slot = 0,
        .guest_phys_addr = 0,
        .memory_size = gmem.size,
        .guest_memfd = gmem_fd,
        .guest_memfd_offset = 0,
    };
    ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION2, &region);

    return vm_fd;
}

KVM 호스트 측 SEV-SNP 흐름

/* arch/x86/kvm/svm/sev.c — SNP VM 생성 상세 */
int sev_vm_init(struct kvm *kvm)
{
    struct kvm_sev_info *sev = &to_kvm_svm(kvm)->sev_info;
    int ret;

    /* ASID 할당 */
    ret = sev_asid_new(sev);
    if (ret)
        return ret;

    /* PSP에 LAUNCH_START 명령 전송 */
    struct sev_data_launch_start start = {
        .handle = 0,
        .policy = sev->policy,
    };
    ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_START,
                        &start, &sev->error);

    /* 초기 메모리 로드: LAUNCH_UPDATE_DATA */
    /* 각 페이지가 측정값(MEASUREMENT)에 포함됨 */

    /* SNP의 경우: SNP_LAUNCH_START → SNP_LAUNCH_UPDATE → */
    /* SNP_LAUNCH_FINISH (측정값 봉인) */
    return ret;
}

/* vCPU 실행 루프 — SEV-SNP 특화 처리 */
static int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
{
    struct ghcb *ghcb = svm_get_ghcb(vcpu);
    u64 exit_code = ghcb->save.sw_exit_code;

    switch (exit_code) {
    case SVM_VMGEXIT_PSC:
        return snp_handle_page_state_change(vcpu);
    case SVM_VMGEXIT_GUEST_REQUEST:
        return snp_handle_guest_request(vcpu);
    case SVM_VMGEXIT_EXT_GUEST_REQUEST:
        return snp_handle_ext_guest_request(vcpu);
    case SVM_VMGEXIT_AP_CREATION:
        return snp_handle_ap_creation(vcpu);
    default:
        pr_warn_ratelimited(
            "SNP: unhandled VMGEXIT: 0x%llx\n",
            exit_code);
        return -EINVAL;
    }
}

guest_memfd 내부 구현

/* virt/kvm/guest_memfd.c — fd-based private memory */
static long kvm_gmem_allocate(
    struct inode *inode,
    loff_t offset, loff_t len)
{
    struct folio *folio;
    pgoff_t index;

    for (index = offset >> PAGE_SHIFT;
         index < (offset + len) >> PAGE_SHIFT; index++) {
        /* shmem에서 folio 할당 */
        folio = shmem_get_folio(inode, index);

        /* 핵심: 호스트 프로세스의 page table에 매핑하지 않음 */
        /* folio는 KVM 내부에서만 참조, QEMU는 접근 불가 */

        /* folio를 하드웨어에 등록 */
        /* SEV-SNP: RMP에 guest-owned로 설정 */
        /* TDX: SEPT에 매핑, HKID 키로 암호화 */
        kvm_gmem_prepare(kvm, folio);
    }
    return 0;
}

/* guest_memfd의 핵심 보안 속성 */
/* 1. mmap 불가: 호스트 유저스페이스에서 접근 차단 */
/* 2. read/write 불가: 호스트 커널에서도 내용 접근 차단 */
/* 3. fork 시 공유 안 됨: CoW 방지 */
/* 4. swap out 불가: 평문이 디스크에 기록되는 것 방지 */
guest_memfd vs memfd_secret: memfd_secret()은 호스트의 유저스페이스가 자체 비밀을 보호하는 용도이고, guest_memfd는 KVM 게스트의 private 메모리를 호스트로부터 보호하는 용도입니다. 구현은 다르지만, 둘 다 "직접 매핑에서 제외"라는 공통 원리를 사용합니다.

virtio & Bounce Buffer

기밀 VM에서 virtio I/O는 shared 메모리를 통해 수행되어야 합니다. private 메모리의 DMA 요청은 SWIOTLB bounce buffer를 거쳐 shared 영역으로 복사됩니다. 이 과정은 게스트 커널의 DMA 계층에서 자동으로 처리되며, 장치 드라이버(Device Driver)는 기밀 VM 여부를 인식하지 않아도 됩니다(투명 처리). 그러나 bounce 복사는 성능 비용이 있으므로, I/O 집약적 워크로드에서는 이것이 주요 병목(Bottleneck)이 됩니다.

/* kernel/dma/swiotlb.c — 기밀 VM을 위한 bounce buffer */
phys_addr_t swiotlb_tbl_map_single(
    struct device *dev,
    phys_addr_t orig_addr,
    size_t mapping_size,
    enum dma_data_direction dir)
{
    unsigned long flags;
    phys_addr_t tlb_addr;
    int index;

    /* shared(비암호화) bounce buffer에서 슬롯 할당 */
    index = swiotlb_find_slots(dev, orig_addr, mapping_size);

    tlb_addr = slot_addr(mem->start, index);

    /* DMA_TO_DEVICE: private → shared 복사 */
    if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)
        swiotlb_bounce(dev, tlb_addr, mapping_size,
                       DMA_TO_DEVICE);

    return tlb_addr;
}
SWIOTLB 크기 주의: 기밀 VM에서는 모든 DMA가 bounce buffer를 경유하므로, 기본 SWIOTLB 크기(64MB)가 부족할 수 있습니다. 커널 파라미터 swiotlb=131072(256MB)로 확대가 권장됩니다. 네트워크 집약적 워크로드에서는 더 큰 값이 필요합니다.

SWIOTLB 내부 구조

기밀 VM의 SWIOTLB는 부팅 초기에 shared(비암호화) 메모리 영역을 확보하고, 모든 DMA 연산을 이 영역을 통해 중계합니다.

/* kernel/dma/swiotlb.c — CoCo 전용 SWIOTLB 초기화 */
void __init swiotlb_init_remap(bool addressing_limited,
                               unsigned int flags,
                               int (*remap)(void *, unsigned long))
{
    size_t alloc_size;
    void *vaddr;

    /* 기밀 VM에서는 기본 크기를 키움 */
    if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT))
        swiotlb_adjust_nareas(num_possible_cpus());

    alloc_size = default_nslabs << IO_TLB_SHIFT;

    /* bounce buffer 메모리 할당 */
    vaddr = memblock_alloc(alloc_size, PAGE_SIZE);

    /* 이 영역을 shared(비암호화)로 전환 */
    /* SEV: C-bit 클리어, TDX: S-bit 설정 */
    set_memory_decrypted((unsigned long)vaddr,
                         alloc_size >> PAGE_SHIFT);
}

/* DMA 경로: private 데이터 → bounce → 장치 */
/*
 * [Guest Private Memory] ──memcpy──→ [Shared Bounce Buffer]
 *     (암호화됨)                          (비암호화)
 *                                           │
 *                                    DMA to device
 *                                           │
 *                                    [I/O 장치]
 */

virtio 성능 최적화

기법설명효과
shared vringvirtqueue 자체를 shared 메모리에 배치vring 접근 시 bounce 불필요
Large SWIOTLBbounce buffer 크기 증가슬롯 부족 방지
DMA 대역폭(Bandwidth)bounce 복사 최적화 (memcpy_toio)CPU 오버헤드(Overhead) 감소
vDPA하드웨어 가속 virtiobounce 우회 (제한적)

성능 오버헤드

기밀 컴퓨팅의 주요 성능 비용은 메모리 암호화, bounce buffer I/O, 페이지 상태 전환에서 발생합니다. 성능 오버헤드의 대부분은 I/O 경로에서 발생하며, 순수 CPU/메모리 연산에서는 오버헤드가 미미합니다. 이는 AES-XTS 엔진이 메모리 컨트롤러에 통합되어 거의 라인 레이트로 동작하기 때문입니다.

성능 측정 시 주의: 기밀 VM의 성능을 측정할 때는 워밍업(warm-up) 단계가 중요합니다. Lazy accept가 활성화된 경우, 최초 메모리 접근 시 PAGE.ACCEPT/PVALIDATE 오버헤드가 포함되므로 초기 측정값이 높게 나올 수 있습니다. 정확한 벤치마크를 위해 최소 2회 이상 반복 실행하세요.
항목오버헤드원인완화 방법
메모리 암호화1~3%AES-XTS 엔진 레이턴시파이프라인(Pipeline) 최적화 (Gen4+에서 개선)
SWIOTLB bounce5~15%DMA 데이터 복사shared vring, 대형 bounce buffer
TLB flush2~5%private↔shared 전환 시 TLB 무효화전환 빈도 최소화
VM Exit3~8%#VC/TDCALL/RSI 처리 비용paravirt 최적화, 배치 처리
페이지 검증부팅 시PVALIDATE/PAGE.ACCEPTlazy accept (on-demand)
증명50~200ms/회PSP/QE 암호 연산캐싱, 배치 증명

벤치마크 비교

# 기밀 VM 성능 측정 (sysbench 메모리)
# 비기밀 VM
sysbench memory --memory-block-size=4K --threads=8 run
# 결과: 8192.34 MiB/sec

# SEV-SNP VM (동일 설정)
sysbench memory --memory-block-size=4K --threads=8 run
# 결과: 7946.12 MiB/sec (~3% 오버헤드)

# 네트워크 I/O (iperf3)
# 비기밀: 23.8 Gbps | SEV-SNP: 19.2 Gbps (~19% 오버헤드, bounce buffer)

# 디스크 I/O (fio 4K random read, virtio-blk)
# 비기밀: 245K IOPS | SEV-SNP: 208K IOPS (~15% 오버헤드)
성능 최적화 팁:
  • CPU 바운드 워크로드는 오버헤드 최소(1~3%). I/O 집약적 워크로드에서 오버헤드가 큼
  • EPYC Gen4(Genoa)+ / Sapphire Rapids+에서 하드웨어 최적화로 오버헤드 감소
  • lazy accept 활성화: 부팅 시간 단축 (온디맨드 페이지 검증)
  • 대형 페이지(2MB) 사용: RMP/SEPT 항목 수 및 TLB 미스 감소

부팅 시간 비교

시나리오비기밀 VMSEV-SNP (전체 accept)SEV-SNP (lazy accept)TDX (lazy accept)
4GB RAM, 4 vCPU~1.2s~3.8s~1.5s~1.4s
16GB RAM, 8 vCPU~1.5s~12s~1.8s~1.7s
64GB RAM, 16 vCPU~2.0s~45s~2.5s~2.3s
256GB RAM, 32 vCPU~3.0s~180s~4.0s~3.5s

워크로드별 오버헤드 분석

워크로드 유형SEV-SNP 오버헤드TDX 오버헤드병목 원인
CPU 연산 (SPEC CPU)1~2%1~2%메모리 암호화 레이턴시
메모리 집약 (STREAM)2~4%2~3%AES 엔진 대역폭
Redis (인메모리 DB)3~5%2~4%네트워크 bounce + 메모리
PostgreSQL (OLTP)5~8%4~7%디스크 I/O bounce
iperf3 (네트워크)15~20%12~18%SWIOTLB bounce 복사
fio (블록 I/O)10~15%8~12%bounce buffer + 인터럽트
ML 추론 (GPU 없음)2~3%2~3%CPU 바운드
Nginx (웹서버)5~10%4~8%네트워크 I/O + TLS
# 기밀 VM 성능 프로파일링

# 1. SWIOTLB 사용량 모니터링
watch -n1 cat /sys/kernel/debug/swiotlb/io_tlb_used

# 2. #VC / VE 빈도 측정 (perf)
perf stat -e 'kvm:kvm_exit' -e 'exceptions:page_fault_kernel' \
  -p $(pgrep qemu-system) -- sleep 10

# 3. 메모리 암호화 오버헤드 (mbw 도구)
mbw -q -n 10 256
# 비기밀: 12,345 MiB/s | SNP: 12,012 MiB/s (-2.7%)

# 4. Page accept 소요 시간
dmesg | grep -i "accepted\|unaccepted"
# 출력 예: Accepted memory: 0x0 - 0x100000000 (4096 MB) in 1.2s

라이브 마이그레이션

기밀 VM의 라이브 마이그레이션은 하이퍼바이저가 메모리를 읽을 수 없으므로, 전통적 마이그레이션과 근본적으로 다릅니다. 암호화된 상태로 전송하고 목적지에서 재검증하는 프로토콜이 필요합니다. 마이그레이션은 기밀 컴퓨팅의 가장 어려운 문제 중 하나이며, 다음 도전 과제를 해결해야 합니다:

플랫폼마이그레이션 방식키 관리Linux 지원
SEV-SNPMigration Agent (VMPL 0)Transport Key Exchangev6.10+ (실험적)
TDXTDX Module 기반 (Service TD)Migration Key Negotiationv6.12+ (패치)
ARM CCA미지원spec 진행 중

SEV-SNP 마이그레이션 프로토콜

/* SEV-SNP 마이그레이션 흐름 (개념 코드) */

/* 1단계: 소스 호스트 — Transport Key 교환 */
sev_cmd(SNP_SEND_START, &send_start);
/* PSP가 Transport Encryption Key(TEK) 파생 */

/* 2단계: 페이지 전송 (반복) */
for (page = 0; page < total_pages; page++) {
    /* PSP가 페이지를 TEK로 재암호화 → 전송 가능 형태 */
    sev_cmd(SNP_SEND_UPDATE_DATA, &send_data);
    /* 하이퍼바이저가 네트워크로 전송 (암호문만 처리) */
    migrate_page(encrypted_data);
}

/* 3단계: 목적지 호스트 — 수신 및 복원 */
sev_cmd(SNP_RECEIVE_START, &recv_start);
for (page = 0; page < total_pages; page++) {
    /* 목적지 PSP가 TEK로 복호화 → 새 VM 키로 재암호화 */
    sev_cmd(SNP_RECEIVE_UPDATE_DATA, &recv_data);
}
sev_cmd(SNP_RECEIVE_FINISH, &recv_finish);
마이그레이션 제약: 기밀 VM 마이그레이션은 아직 초기 단계입니다. 소스와 목적지가 동일 CPU 세대(동일 TCB 버전)여야 하며, 마이그레이션 중 VM이 일시 중지되는 시간이 비기밀 VM보다 길 수 있습니다. TDX의 경우 Service TD(Migration TD)가 마이그레이션 에이전트 역할을 합니다.

TDX 라이브 마이그레이션

TDX의 라이브 마이그레이션은 Migration TD(Service TD)라는 특수 TD를 사용합니다. Migration TD가 소스/목적지 TDX Module 사이의 키 교환과 메모리 전송을 중재합니다.

/* TDX 라이브 마이그레이션 프로토콜 (개념 코드) */

/* 소스 호스트 */
/* 1. Migration TD 생성 및 키 교환 */
seamcall(TDH_MIG_STREAM_CREATE, src_td, mig_td);

/* 2. 페이지 내보내기 (암호화 상태 유지) */
for (gpa = 0; gpa < td_memory_size; gpa += PAGE_SIZE) {
    /* TDX Module이 페이지를 Migration Key로 재암호화 */
    seamcall(TDH_MR_FINALIZE, src_td);
    seamcall(TDH_EXPORT_PAGE, src_td, gpa, &enc_page);

    /* 암호화된 페이지를 네트워크로 전송 */
    send_encrypted_page(dest_host, enc_page);
}

/* 목적지 호스트 */
/* 3. 새 TD 생성 + 페이지 임포트 */
seamcall(TDH_MNG_CREATE, dest_td, hkid);
for (gpa = 0; gpa < td_memory_size; gpa += PAGE_SIZE) {
    seamcall(TDH_IMPORT_PAGE, dest_td, gpa, &enc_page);
    /* TDX Module이 Migration Key로 복호화 → 새 HKID 키로 재암호화 */
}

마이그레이션 시 증명 연속성

기밀 VM이 마이그레이션된 후에도 동일한 워크로드임을 증명할 수 있어야 합니다. 마이그레이션 에이전트가 소스와 목적지 양쪽의 증명 보고서를 체인으로 연결하여 연속성을 보장합니다.

단계SEV-SNPTDX
키 교환PSP 간 Transport Key 교환Migration TD 기반
페이지 전송PSP가 재암호화 → 네트워크 → PSP가 복호화TDX Module이 재암호화/복호화
vCPU 상태VMCB Save Area 암호화 전송TD VMCS 암호화 전송
증명 유지동일 MEASUREMENT 유지동일 MRTD + RTMR 유지
다운타임~100ms~1s (추가)~50ms~500ms (추가)

기밀 컨테이너

기밀 컨테이너(Confidential Containers, CoCo)는 기밀 VM 안에서 컨테이너 워크로드를 실행합니다. CNCF(Cloud Native Computing Foundation) 산하 프로젝트로, Kata Containers의 microVM 방식을 활용하여, 각 Pod가 독립적인 기밀 VM 안에서 실행됩니다. 기존 Kubernetes 워크플로우를 최소한으로 변경하면서 기밀 컴퓨팅의 보안 이점을 활용할 수 있습니다.

CoCo vs 전통적 기밀 VM: 전통적 기밀 VM은 IaaS 계층에서 전체 VM을 보호합니다. CoCo는 이를 한 단계 더 나아가 컨테이너 이미지 자체를 암호화하고, 증명 성공 후에만 이미지를 복호화하여 실행합니다. 이를 통해 컨테이너 레지스트리, 호스트 관리자, CSP 직원 모두로부터 워크로드를 보호합니다.
Kubernetes (kubelet + containerd/CRI-O) RuntimeClass: kata-cc (기밀 Pod 스케줄링) Kata Containers Runtime (kata-runtime) microVM 생성 + 컨테이너 이미지 전달 (암호화된 형태) 호스트 VMM (비신뢰) QEMU/cloud-hypervisor + KVM 기밀 VM (SEV-SNP / TDX) kata-agent 컨테이너 실행 관리 attestation-agent 원격 증명 + 키 릴리스 Container 1 App + 데이터 Container 2 App + 데이터 Sidecar 로그/메트릭 게스트 Linux 커널 (기밀 보호)

CoCo 아키텍처 구성 요소

구성 요소역할위치
kata-runtimeCRI shimv2, 기밀 VM 생명주기 관리호스트
kata-agent게스트 내 컨테이너 실행 (containerd의 리모트 에이전트)기밀 VM 내부
attestation-agent (AA)증명 보고서 생성 → KBS에 전송 → 키/시크릿 수신기밀 VM 내부
KBS (Key Broker Service)증명 검증 후 키/비밀 릴리스신뢰 영역 (별도 서버)
AS (Attestation Service)증명 정책 평가 (OPA/Rego)신뢰 영역
image-rs암호화 컨테이너 이미지 복호화기밀 VM 내부

CoCo 증명 흐름 상세

기밀 컨테이너의 증명은 Pod 시작 시 자동으로 수행됩니다. attestation-agent가 하드웨어 증명 보고서를 생성하고, KBS에 전송하여 컨테이너 이미지 복호화 키를 획득합니다.

/* CoCo 증명 흐름 (의사 코드) */

/* 1. Pod 시작 시 attestation-agent 초기화 */
aa_init() {
    /* 하드웨어 증명 드라이버 탐지 */
    if (access("/dev/sev-guest"))
        tee_type = SEV_SNP;
    else if (access("/dev/tdx_guest"))
        tee_type = TDX;
}

/* 2. KBS에 증명 세션 시작 */
aa_attestation_begin() {
    /* Challenge-Response 프로토콜 */
    nonce = kbs_get_challenge();

    /* 하드웨어 증명 보고서 생성 (nonce 포함) */
    evidence = get_hw_evidence(nonce);

    /* KBS에 Evidence 전송 → 검증 → 토큰 수신 */
    token = kbs_attest(evidence);
}

/* 3. 키 릴리스 및 이미지 복호화 */
aa_get_resource() {
    /* 증명 토큰으로 컨테이너 이미지 키 요청 */
    key = kbs_get_resource(token, "image-key");

    /* 암호화된 OCI 이미지 복호화 */
    image_decrypt(encrypted_layers, key);
}

암호화 컨테이너 이미지

CoCo에서는 컨테이너 이미지 자체를 암호화하여, 호스트가 이미지 내용을 볼 수 없게 합니다. OICF(OCI Image Encryption) 스펙을 따르며, 이미지 레이어가 AES-256-GCM으로 암호화됩니다.

# 컨테이너 이미지 암호화 (skopeo + ocicrypt)
# 1. 키 생성
openssl rand -out image_key.bin 32

# 2. 이미지 암호화
skopeo copy \
  docker://myregistry/myapp:latest \
  docker://myregistry/myapp:encrypted \
  --encryption-key jwe:image_pubkey.pem

# 3. KBS에 복호화 키 등록
kbs-client --url https://kbs.example.com \
  set-resource \
  --path "default/image-key/myapp" \
  --file image_key.bin
# Kubernetes에서 기밀 Pod 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: confidential-app
spec:
  runtimeClassName: kata-cc   # CoCo 런타임 클래스
  containers:
  - name: app
    image: ghcr.io/my-org/secret-app:latest
    resources:
      limits:
        memory: "512Mi"
        cpu: "1"
  - name: attestation-sidecar
    image: ghcr.io/confidential-containers/attestation-agent:latest
EOF

GPU TEE

GPU의 기밀 컴퓨팅은 CPU TEE를 GPU로 확장하여, AI/ML 워크로드의 모델과 데이터를 GPU 메모리에서도 보호합니다. LLM(Large Language Model) 추론, 의료 영상 분석, 금융 모델링 등 민감한 AI 워크로드에서 핵심적입니다. GPU 없이는 기밀 VM에서 AI 워크로드의 데이터는 보호되지만, GPU로 전송되는 순간 보호가 해제되는 "보안 갭"이 존재했습니다. GPU TEE는 이 갭을 해소합니다.

GPU TEE 제약사항: 현재 GPU TEE는 NVIDIA H100+와 AMD MI300+에서만 지원됩니다. 소비자용 GPU(GeForce, Radeon)에서는 사용할 수 없습니다. 또한 GPU TEE 모드에서는 디스플레이 출력이 비활성화되고, 일부 CUDA/ROCm 기능에 제약이 있을 수 있습니다.
CPU TEE (기밀 VM) AI 프레임워크 PyTorch/TensorFlow CUDA/ROCm 호출 GPU 드라이버 NVIDIA/AMD 암호화 채널 GPU TEE (보호 영역) GPU 컴퓨트 커널 실행 암호화 VRAM GPU 증명 GPU Attestation Report 생성 AES 암호화 PCIe 채널 GPU TEE 벤더별 현황 NVIDIA Confidential Computing (H100+) GPU TEE 모드: on-chip AES-256-GCM 메모리 암호화 CPU TEE(SEV-SNP/TDX) ↔ GPU TEE: SPDM 인증 + IDE 암호화 GPU Attestation: NVIDIA RIM Service + Verifier AMD SEV-TIO (EPYC + MI300+) TIO (Trusted I/O): SEV-SNP를 PCIe 장치로 확장 GPU 메모리를 RMP로 보호 (VM의 private 영역 확장) TDISP 프로토콜: PCIe 6.0 TEE Device Interface
GPU TEE 핵심 프로토콜:
  • SPDM (Security Protocol and Data Model): GPU 인증 및 세션 키 교환
  • IDE (Integrity and Data Encryption): PCIe 링크 암호화 (AES-256-GCM)
  • TDISP (TEE Device Interface Security Protocol): PCIe 6.0 TEE 장치 표준

NVIDIA H100 CC 모드 설정

# NVIDIA GPU CC 모드 활성화
# 1. GPU CC 모드 확인
nvidia-smi conf-compute -grs
# GPU 0: CC Mode: OFF

# 2. CC 모드 활성화 (재부팅 필요)
nvidia-smi conf-compute -srs on -i 0
# Confidential Computing mode set to ON

# 3. 재부팅 후 확인
nvidia-smi conf-compute -grs
# GPU 0: CC Mode: ON

# 4. GPU 증명 확인
nvidia-smi conf-compute -gas
# Attestation Status: ATTESTED

# 5. GPU 증명 검증 (NVIDIA NRAS 사용)
nv-attestation-sdk verify \
  --gpu-evidence gpu_evidence.bin \
  --nras-url https://nras.attestation.nvidia.com

GPU TEE 데이터 흐름

기밀 VM에서 GPU를 사용할 때, CPU TEE와 GPU TEE 사이의 데이터는 PCIe IDE(Integrity and Data Encryption)로 보호됩니다. SPDM 프로토콜로 상호 인증 후 세션 키를 교환하고, 이후 모든 PCIe 트래픽을 AES-256-GCM으로 암호화합니다.

/* GPU TEE 데이터 흐름 (개념) */

/* 1. GPU 인증 (SPDM) */
/*    CPU TEE ←→ GPU: SPDM GET_CERTIFICATE, CHALLENGE */
/*    결과: 상호 인증 + 세션 키 교환 */

/* 2. PCIe IDE 키 프로그래밍 */
/*    IDE 스트림 키를 PCIe 컨트롤러에 설정 */
/*    모든 TLP(Transaction Layer Packet) 암호화 */

/* 3. 데이터 전송 (암호화된 PCIe) */
/*    CPU 메모리(private) → PCIe(IDE 암호화) → GPU VRAM(암호화) */
/*    GPU VRAM(암호화) → PCIe(IDE 암호화) → CPU 메모리(private) */

/* 4. GPU 컴퓨트 */
/*    GPU 내부에서 복호화 → 연산 → 재암호화 */
/*    외부에서 VRAM 내용 접근 불가 */

SEV-TIO (Trusted I/O) 개요

AMD SEV-TIO는 SEV-SNP를 PCIe 장치로 확장하여, GPU 등의 가속기 메모리를 RMP로 보호합니다. PCIe 6.0의 TDISP 프로토콜을 구현하며, 장치 메모리를 게스트 VM의 private 영역으로 편입시킵니다.

기능NVIDIA CC (H100)AMD SEV-TIO
메모리 보호GPU 자체 AES-256-GCMRMP 확장 (CPU 관리)
링크 보호PCIe IDEPCIe IDE (TDISP)
인증SPDM + NVIDIA NRASSPDM + SEV-SNP 통합 증명
DMA 보호Bounce buffer 우회직접 DMA (RMP 보호)
지원 상태H100+ 프로덕션MI300+ 개발 중

CSP별 구현

주요 CSP(Cloud Service Provider)별 기밀 컴퓨팅 서비스 현황입니다.

CSP서비스명기반 기술VM 타입증명
AzureDCasv5/DCadsv5SEV-SNPAMD EPYC (Milan/Genoa)MAA (Microsoft Azure Attestation)
AzureDCesv5/DCedsv5TDXIntel SPRMAA
GCPConfidential VMs (C2D/C3D)SEV-SNP / TDXAMD/IntelGoogle Confidential Space
AWSNitro EnclavesNitro Hypervisor자체 격리Nitro Attestation (NSM)
AWSi-SEV-SNP (Preview)SEV-SNPAMD EPYCAMD KDS + AWS RA
IBM CloudHyper Protect Virtual ServerIBM Secure ExecutionIBM z16/LinuxONEIBM Attestation
OCIConfidential ComputingSEV-ES/SEV-SNPAMD EPYCAMD KDS

CSP 기밀 컴퓨팅 비교 상세

항목Azure DCasv5GCP C3D CCAWS Nitro Enclaves
기반 기술SEV-SNP (풀 VM)SEV-SNP/TDX (풀 VM)Nitro 독자 격리
vCPU 범위2~96 vCPU4~360 vCPU부모 인스턴스 공유
메모리8~384 GB16~2880 GB256 MB~부모 절반
증명 서비스MAA (REST API + JWT)Confidential SpaceNSM (COSE)
컨테이너 지원AKS CC + KataGKE CC NodesEnclave 전용
GPU 지원NCCadsH100v5 (H100)A3 Mega (H100)미지원
디스크 암호화OS 디스크 기밀 암호화CMEK + CC 지원KMS 봉인
가격 프리미엄~6~10%~5~8%추가 비용 없음
AWS Nitro Enclaves의 차이점: AWS Nitro Enclaves는 SEV/TDX 기반이 아닌, Nitro Hypervisor의 독자적 격리 기술을 사용합니다. 부모 EC2 인스턴스의 vCPU/메모리 일부를 분리하여 별도의 보안 환경을 생성합니다. 장점은 추가 비용이 없고 기존 인스턴스에서 바로 사용 가능하다는 점이며, 단점은 네트워크 접근이 제한(vsock만 가능)되고 지속적 스토리지가 없다는 점입니다. 최근 AWS도 SEV-SNP 기반 기밀 VM(i-SEV-SNP)을 프리뷰로 제공하기 시작했습니다.

Azure SEV-SNP 예시

GCP Confidential VM 예시

# GCP에서 SEV-SNP 기밀 VM 생성
gcloud compute instances create my-cc-vm \
  --zone=us-central1-a \
  --machine-type=n2d-standard-4 \
  --min-cpu-platform="AMD Milan" \
  --confidential-compute-type=SEV_SNP \
  --image-family=ubuntu-2404-lts-amd64 \
  --image-project=confidential-vm-images \
  --maintenance-policy=TERMINATE

# 기밀 VM 상태 확인
gcloud compute instances describe my-cc-vm \
  --format="value(confidentialInstanceConfig)"
# enableConfidentialCompute: true
# confidentialInstanceType: SEV_SNP

# GKE에서 기밀 노드풀 생성
gcloud container node-pools create cc-pool \
  --cluster=my-cluster \
  --machine-type=n2d-standard-4 \
  --enable-confidential-nodes \
  --num-nodes=3

Azure SEV-SNP 예시

# Azure CLI로 SEV-SNP 기밀 VM 생성
az vm create \
  --resource-group myRG \
  --name myConfVM \
  --image Canonical:0001-com-ubuntu-confidential-vm-jammy:22_04-lts-cvm:latest \
  --size Standard_DC4as_v5 \
  --security-type ConfidentialVM \
  --os-disk-security-encryption-type VMGuestStateOnly \
  --enable-secure-boot true \
  --enable-vtpm true

# 증명 토큰 확인 (게스트 내부)
curl -H Metadata:true \
  "http://169.254.169.254/metadata/THIM/amd/certification" \
  | python3 -m json.tool

증명 서비스

원격 증명 서비스는 기밀 VM의 증명 보고서를 검증하고, 검증 성공 시 키/시크릿을 릴리스하는 인프라입니다. 증명 서비스는 Reference Values(기대 측정값)와 실제 Evidence(증명 보고서)를 비교하여 "이 VM이 기대한 소프트웨어 스택으로 실행 중인지" 판정합니다.

서비스지원 플랫폼프로토콜정책 엔진(Policy Engine)
MAA (Microsoft Azure Attestation)SEV-SNP, TDX, SGXREST API + JWTAzure Policy (JSON)
Intel Trust AuthorityTDX, SGXREST API + JWTIntel 관리 정책
AWS Nitro AttestationNitro EnclavesNSM + COSEPCR 기반
Google Confidential SpaceSEV-SNP, TDXOIDC TokenWorkload Identity Pool
KBS (Key Broker Service)SEV-SNP, TDX, CCACoCo 프로토콜OPA/Rego
AMD KDSSEV-SNPVCEK 인증서 배포

Intel Trust Authority 사용 예시

# Intel Trust Authority CLI (trustauthority-cli)

# 1. TDX 게스트에서 Quote 생성
trustauthority-cli quote --tee tdx --user-data $(echo -n "nonce123" | base64)
# Output: base64 encoded TDX Quote

# 2. Intel Trust Authority에 Quote 전송 및 검증
trustauthority-cli verify-quote \
  --api-url https://api.trustauthority.intel.com \
  --api-key $ITA_API_KEY \
  --quote quote.bin

# 3. 검증 결과 (JWT 토큰)
# {
#   "attester_type": "TDX",
#   "tdx_mrtd": "a1b2c3...",
#   "tdx_rtmr0": "d4e5f6...",
#   "tdx_seamsvn": 3,
#   "evaluation_status": "healthy",
#   "exp": 1720000000
# }

# 4. JWT를 사용하여 KMS에서 키 획득
curl -H "Authorization: Bearer $JWT_TOKEN" \
  https://kms.example.com/v1/keys/my-encryption-key

CoCo 통합 증명 프레임워크

CNCF Confidential Containers 프로젝트는 CoCo AS(Attestation Service)와 KBS(Key Broker Service)를 통합한 증명 프레임워크를 제공합니다. 이 프레임워크는 SEV-SNP, TDX, CCA 등 다양한 TEE를 통합 지원합니다.

# CoCo KBS 배포 (Kubernetes)
kubectl apply -f https://github.com/confidential-containers/\
trustee/releases/latest/deploy/kbs.yaml

# 리소스 등록 (컨테이너 이미지 키)
kbs-client config \
  --auth-private-key admin.key set-resource \
  --path "default/images/myapp-key" \
  --resource-file image_key.bin

# 증명 정책 업로드
kbs-client config \
  --auth-private-key admin.key set-attestation-policy \
  --policy-file reference_values.rego

RATS (Remote ATtestation procedureS) 아키텍처

IETF RATS(RFC 9334)는 원격 증명의 표준 아키텍처를 정의합니다.

/* RATS 아키텍처의 3개 역할 */

/* 1. Attester — 증명 증거 생성 (기밀 VM) */
/*    Evidence = HW Attestation Report + 추가 클레임 */

/* 2. Verifier — 증거 검증 (MAA, ITA, KBS) */
/*    Input: Evidence + Reference Values + Endorsements */
/*    Output: Attestation Result (pass/fail + 토큰) */

/* 3. Relying Party — 결과에 따라 접근 결정 */
/*    검증 성공 시: 키 릴리스, 데이터 접근 허용 */
/*    검증 실패 시: 접근 거부 */

/* 증명 흐름 모델 */
/* Background Check: Attester → Relying Party → Verifier */
/* Passport:          Attester → Verifier → Token → RP    */

KBS 정책 예시 (OPA/Rego)

# KBS 증명 정책 (Rego 언어)
# reference_values.rego
package policy

import future.keywords.if

default allow := false

# SEV-SNP 증명 정책
allow if {
    input.tee == "sevsnp"
    # 측정값(MEASUREMENT) 검증
    input.tcb.measurement == "expected_measurement_sha384_hex"
    # 최소 TCB 버전 요구
    input.tcb.snp_version >= 14
    input.tcb.microcode >= 209
    # 디버그 모드 비허용
    input.policy.debug == false
    # SMT 정책
    input.policy.smt_allowed == true
}

# TDX 증명 정책
allow if {
    input.tee == "tdx"
    input.tcb.mrtd == "expected_mrtd_sha384_hex"
    input.tcb.tdx_module_version >= "1.5.0"
    input.attributes.debug == false
}

증명 흐름 통합: Passport vs Background Check

모델흐름장점단점
PassportAttester → Verifier → Token → RPRP가 검증 로직 불필요, 오프라인 가능토큰 만료 관리, Verifier 단일 장애점
Background CheckAttester → RP → VerifierRP가 실시간 검증, 최신성 보장RP와 Verifier 간 통신 필요

커널 설정 종합

기밀 컴퓨팅을 위한 커널 설정은 호스트(KVM 하이퍼바이저)와 게스트(기밀 VM) 양쪽에서 필요합니다. 잘못된 설정은 기밀 VM이 생성되지 않거나, 부팅 실패, 성능 저하의 원인이 됩니다.

호스트 (KVM) 설정

# SEV/SEV-ES/SEV-SNP 호스트 지원
CONFIG_KVM_AMD_SEV=y
CONFIG_CRYPTO_DEV_CCP=y
CONFIG_CRYPTO_DEV_CCP_DD=y
CONFIG_CRYPTO_DEV_SP_PSP=y

# TDX 호스트 지원
CONFIG_INTEL_TDX_HOST=y
CONFIG_KVM_INTEL_TDX=y

# guest_memfd (fd-based private memory)
CONFIG_KVM_GENERIC_PRIVATE_MEM=y

# IOMMU (기밀 I/O 보호)
CONFIG_IOMMU_SUPPORT=y
CONFIG_AMD_IOMMU=y
CONFIG_INTEL_IOMMU=y

게스트 설정

# 공통 CoCo 게스트 지원
CONFIG_CONFIDENTIAL_COMPUTING=y

# AMD SEV 게스트
CONFIG_AMD_MEM_ENCRYPT=y
CONFIG_AMD_MEM_ENCRYPT_ACTIVE_BY_DEFAULT=y
CONFIG_SEV_GUEST=y          # /dev/sev-guest 장치

# Intel TDX 게스트
CONFIG_INTEL_TDX_GUEST=y
CONFIG_TDX_GUEST_DRIVER=y   # /dev/tdx_guest 장치

# 공통 인프라
CONFIG_SWIOTLB=y            # bounce buffer (필수)
CONFIG_UNACCEPTED_MEMORY=y  # lazy accept 지원
CONFIG_EFI=y                # OVMF 연동

# 디버깅
CONFIG_DYNAMIC_DEBUG=y
CONFIG_CRYPTO_DEV_CCP_DEBUGFS=y

ARM CCA 게스트 설정

# ARM CCA Realm 게스트 지원 (실험적)
CONFIG_ARM64_RME=y
CONFIG_KVM_ARM_RME=y

# RMM 펌웨어 빌드 (TF-RMM)
git clone https://git.trustedfirmware.org/TF-RMM/tf-rmm.git
cd tf-rmm
cmake -DRMM_CONFIG=fvp_defcfg -B build
cmake --build build

sysfs 인터페이스

# SEV/SNP sysfs 인터페이스 (호스트)
/sys/devices/virtual/misc/sev/
├── fw_version       # PSP 펌웨어 버전
├── api_major        # SEV API 메이저 버전
├── api_minor        # SEV API 마이너 버전
├── build            # PSP 빌드 번호
└── asids            # 사용 가능한 ASID 수

# TDX sysfs 인터페이스 (호스트)
/sys/firmware/tdx/
├── tdx_module_version  # TDX Module 버전
└── seamldr_version     # P-SEAMLDR 버전

# CoCo 게스트 감지 (게스트 내부)
/sys/devices/system/cpu/
├── sev              # 1 = SEV 활성
├── sev_es           # 1 = SEV-ES 활성
├── sev_snp          # 1 = SEV-SNP 활성
└── tdx              # 1 = TDX 활성

# SWIOTLB 정보
/sys/kernel/debug/swiotlb/
├── io_tlb_nslabs    # 총 슬롯 수
├── io_tlb_used      # 사용 중인 슬롯
└── io_tlb_overflow  # 오버플로우 횟수 (0이어야 정상)

커널 부트 파라미터

파라미터설명기본값
mem_encrypt=onSME/SEV 메모리 암호화 활성화off (호스트), on (SEV 게스트)
sev=1KVM SEV 지원 활성화 (호스트)1 (CONFIG_KVM_AMD_SEV 시)
swiotlb=131072bounce buffer 크기 (256MB)65536 (128MB)
kvm_amd.sev=1KVM AMD SEV 모듈 활성화1
kvm_amd.sev_es=1KVM AMD SEV-ES 활성화1
kvm_amd.sev_snp=1KVM AMD SEV-SNP 활성화1
tdx=1TDX 호스트 활성화0

QEMU 실습

개발 환경에서 기밀 VM을 테스트하는 QEMU 설정입니다. 기밀 VM 실습은 반드시 지원 하드웨어(AMD EPYC 7003+, Intel SPR+)가 필요하며, 일반 데스크톱 CPU나 중첩 가상화 환경에서는 실행할 수 없습니다. BIOS/UEFI에서 해당 기능이 활성화되어 있어야 하며, 호스트 커널과 QEMU 모두 적절한 버전이어야 합니다.

호스트 (AMD EPYC / Intel SPR) Linux 호스트 커널 KVM + SEV/TDX 모듈 QEMU 8.2+ -object sev-snp-guest OVMF (AmdSev) 기밀 VM 펌웨어 sevctl / tdxctl 디버깅 도구 기밀 VM (SEV-SNP / TDX) 게스트 Linux + /dev/sev-guest (또는 /dev/tdx_guest) snpguest report → 증명 보고서 생성 dmesg: "Memory Encryption Features active: AMD SEV SEV-ES SEV-SNP"

SEV-SNP QEMU 실행

# 1. 호스트 확인
dmesg | grep -i "sev\|snp"
# SEV: 509 ASIDs | SEV-ES: 100 ASIDs | SEV-SNP supported
cat /sys/module/kvm_amd/parameters/sev_snp
# Y

# 2. OVMF 펌웨어 준비
ls /usr/share/edk2/ovmf/OVMF_CODE.fd  # SEV용 OVMF

# 3. SEV-SNP VM 실행
qemu-system-x86_64 \
  -enable-kvm \
  -cpu EPYC-v4 \
  -machine q35,confidential-guest-support=sev0,memory-backend=ram1 \
  -object memory-backend-memfd,id=ram1,size=4G,share=true \
  -object sev-snp-guest,id=sev0,cbitpos=51,reduced-phys-bits=1,\
policy=0x30000,kernel-hashes=on \
  -drive if=pflash,format=raw,unit=0,file=OVMF_CODE.fd,readonly=on \
  -drive if=pflash,format=raw,unit=1,file=OVMF_VARS.fd \
  -drive file=guest.qcow2,if=virtio \
  -netdev user,id=net0 -device virtio-net-pci,netdev=net0 \
  -smp 4 -m 4G \
  -nographic

TDX QEMU 실행

# 1. TDX 호스트 확인
dmesg | grep -i tdx
# TDX module initialized successfully

# 2. TDX VM 실행
qemu-system-x86_64 \
  -enable-kvm \
  -cpu host \
  -machine q35,kernel-irqchip=split,confidential-guest-support=tdx0,\
memory-backend=ram1 \
  -object memory-backend-memfd-private,id=ram1,size=4G \
  -object tdx-guest,id=tdx0 \
  -drive if=pflash,format=raw,unit=0,file=OVMF_CODE_TDX.fd,readonly=on \
  -drive file=guest.qcow2,if=virtio \
  -smp 4 -m 4G \
  -nographic

게스트 내 확인

# 게스트 내부에서 기밀 VM 확인
dmesg | grep -i "memory encryption\|sev\|tdx\|coco"
# Memory Encryption Features active: AMD SEV SEV-ES SEV-SNP
# 또는: tdx: Guest detected

# CoCo 정보 확인
cat /sys/devices/system/cpu/sev
# 1

# SNP 증명 보고서 획득 (snpguest 유틸)
snpguest report attestation.bin request.bin --random
snpguest display report attestation.bin

# TDX 증명 보고서 획득
tdx_attest -r tdreport.bin
실습 요구사항: SEV-SNP는 AMD EPYC 7003(Milan)+ CPU, 최신 PSP 펌웨어, Linux 6.2+ 호스트 커널이 필요합니다. TDX는 4th Gen Intel Xeon(Sapphire Rapids)+ CPU, P-SEAMLDR/TDX Module 설치, Linux 6.7+ 호스트 커널이 필요합니다. 일반 데스크톱 CPU에서는 실행할 수 없습니다.

SEV-SNP QEMU 정책 플래그 상세

QEMU 옵션설명프로덕션 권장
policy=0x30000SMT 허용, 디버그 금지기본 프로덕션 설정
policy=0x30001SMT 허용, 디버그 허용개발/테스트용
policy=0x10000SMT 금지, 디버그 금지고보안 환경
kernel-hashes=on커널/initrd 해시(Hash)를 측정값에 포함Direct Boot 시 필수
cbitpos=51C-bit 위치 (Genoa)CPU 세대별 확인
reduced-phys-bits=1C-bit로 사용된 물리 비트1 (일반적)

TDX QEMU 옵션 상세

# TDX QEMU 고급 옵션
qemu-system-x86_64 \
  -enable-kvm \
  -cpu host \
  -machine q35,kernel-irqchip=split,\
confidential-guest-support=tdx0,\
memory-backend=ram1 \
  -object memory-backend-memfd-private,id=ram1,size=8G \
  -object tdx-guest,id=tdx0,\
sept-ve-disable=on,\
debug=off \
  -drive if=pflash,format=raw,unit=0,\
file=OVMF_CODE_TDX.fd,readonly=on \
  -drive if=virtio,format=qcow2,\
file=tdx-guest.qcow2 \
  -device virtio-net-pci,netdev=net0 \
  -netdev user,id=net0 \
  -smp 8 -m 8G \
  -nographic

# sept-ve-disable: EPT violation을 #VE 대신 TD Exit으로 처리
# debug: 디버그 TD 활성화 (프로덕션에서 반드시 off)

SEV vs TDX vs CCA 상세 비교

비교 항목AMD SEV-SNPIntel TDXARM CCA
TCB 구성CPU HW + PSP FW + 게스트CPU HW + TDX Module + 게스트CPU HW + EL3 Monitor + RMM + 게스트
보안 프로세서PSP (ARM Cortex-A5, SoC 내장)TDX Module (SEAM 모드 SW)EL3 Monitor + RMM
메모리 암호화AES-256-XTS (Gen4+)AES-128/256-XTS (MKTME)구현체 종속 (MEC)
키 수ASID당 1개 (최대 509+)HKID당 1개 (최대 ~64)Realm당 1개
무결성 보호RMP (소프트웨어 테이블, CPU 검증)메모리 MAC 태그 (하드웨어)GPT (하드웨어 GPC)
게스트 내 권한 분리VMPL 0~3 (4단계)없음 (TD 전체 단일 권한)Realm EL0/EL1
인터럽트 모델#VC 기반 (GHCB)TD Exit → VMM 에뮬레이션RSI_HOST_CALL
중첩 가상화미지원미지원 (spec 진행 중)미지원
라이브 마이그레이션지원 (Migration Agent)지원 (Service TD)미지원
GPU TEE 연동SEV-TIO (PCIe TDISP)TDX Connect (PCIe IDE)미정
Linux 게스트 지원v5.19+v6.2+v6.9+ (실험적)
Linux 호스트 지원v6.2+v6.7+v6.9+ (실험적)
성숙도프로덕션프로덕션 (CSP)개발 초기

아키텍처 설계 철학 비교

설계 철학의 차이:
  • AMD SEV-SNP — 점진적 진화(SEV→ES→SNP). 기존 KVM 인프라를 최대한 재활용. PSP라는 별도 보안 프로세서에 의존. VMPL로 게스트 내부 권한 분리 가능.
  • Intel TDX — SEAM이라는 새로운 CPU 모드 도입. TDX Module은 Intel이 서명한 소프트웨어로 업데이트 가능. SGX 증명 인프라 재활용.
  • ARM CCA — 4개 세계 모델로 가장 깔끔한 아키텍처. GPT로 하드웨어 수준 격리. 그러나 SoC 제조사 의존성이 높고, 생태계가 아직 초기 단계.

Linux 커널 소스 트리 위치

기능소스 경로설명
SEV 게스트 코어arch/x86/coco/sev/#VC 핸들러, SNP 초기화, GHCB
TDX 게스트 코어arch/x86/coco/tdx/VE 핸들러, TDCALL 래퍼
CoCo 공통arch/x86/coco/core.cCC 벤더 추상화, set_memory_*
KVM SEVarch/x86/kvm/svm/sev.c호스트 SEV/SNP 관리
KVM TDXarch/x86/kvm/vmx/tdx.c호스트 TDX 관리
TDX Module 관리arch/x86/virt/vmx/tdx/TDX Module 초기화, SEAMCALL
CCP/PSP 드라이버drivers/crypto/ccp/PSP 통신, SEV 명령
guest_memfdvirt/kvm/guest_memfd.cfd-based private memory
SWIOTLBkernel/dma/swiotlb.cbounce buffer
SEV 게스트 드라이버drivers/virt/coco/sev-guest//dev/sev-guest
TDX 게스트 드라이버drivers/virt/coco/tdx-guest//dev/tdx_guest
ARM CCAarch/arm64/kvm/rme*.cRealm KVM 지원

향후 전망

기밀 컴퓨팅은 빠르게 진화하고 있습니다. 주요 발전 방향은 다음과 같습니다.

영역현재향후 (2025~2027)
중첩 가상화미지원TDX v2.0에서 중첩 TD 지원 예정
GPU TEEH100/MI300 초기범용 가속기 TEE (NPU, FPGA)
ARM CCAFVP/실험적실리콘 출시 (2025+)
표준화벤더별 APICoRIM, IETF RATS 기반 통합 증명
성능5~15% I/O 오버헤드하드웨어 최적화로 <5% 목표
컨테이너CoCo v0.x프로덕션 GA, 서비스 메시 통합
I/O 가상화bounce bufferTDX Connect/SEV-TIO로 직접 DMA
SVSMcoconut-svsm 초기vTPM 표준화, 라이브 마이그레이션

SEV-SNP vs TDX 인터럽트 처리 비교

항목SEV-SNPTDX
I/O 에뮬레이션#VC → GHCB → VMGEXIT#VE → TDVMCALL
CPUID 처리CPUID 페이지 (펌웨어 검증) + GHCB 폴백CPUID 가상화 (TDX Module 자체 처리)
MSR 접근#VC → GHCB#VE → TDVMCALL (일부 MSR은 TDX Module 직접 처리)
인터럽트 주입AVIC 또는 x2APIC emulPosted Interrupt (제한적 VMM 개입)
타이머(Timer)VMGEXIT 기반Secure APIC Timer (VMM 불필요)
MMIO#VC → GHCB (디코딩 필요)#VE → TDVMCALL (디코딩 필요)

CC 관련 Linux 커밋 히스토리

커널 버전주요 추가 기능
v5.19SEV-SNP 게스트 기본 지원 (boot, #VC, GHCB v2)
v6.0TDX 게스트 기본 지원 (#VE, TDCALL)
v6.2KVM SEV-SNP 호스트 지원, SNP attestation driver
v6.5CONFIG_UNACCEPTED_MEMORY (lazy accept)
v6.6guest_memfd (fd-based private memory), cc_platform_has()
v6.7KVM TDX 호스트 지원, SVSM 인식, SNP PSC 배치
v6.9ARM CCA 실험적 지원, TDX attestation 개선
v6.10SNP 라이브 마이그레이션 초기, TDX 2MB 페이지 accept
v6.12TDX 라이브 마이그레이션, coconut-svsm vTPM 연동

보안 권고 및 CVE

CVE영향완화
CVE-2023-20592 (CacheWarp)SEV/SEV-ES: INVD로 캐시 롤백마이크로코드 업데이트 + SNP RMP
CVE-2021-26311 (SEV ASID aliasing)SEV: ASID 재사용 시 키 혼동SNP의 RMP로 완전 차단
CVE-2019-9836 (SEV insecure encrypt)SEV: XEX 모드 평문 복구 가능성SEV-SNP + AES-256-XTS 전환
CVE-2023-46103 (TDX MKTME leak)TDX: 특정 조건에서 KeyID 누출마이크로코드 업데이트
보안 업데이트 필수: 기밀 컴퓨팅은 하드웨어 보안에 의존하므로, CPU 마이크로코드, PSP/TDX Module 펌웨어, 호스트/게스트 커널을 항상 최신 상태로 유지해야 합니다. 증명 보고서의 TCB 버전을 통해 패치 수준을 원격으로 검증할 수 있습니다.

트러블슈팅

증상원인해결
QEMU: "SEV: failed to create encryption context" PSP 펌웨어 미로드 또는 /dev/sev 권한 부족 ls -la /dev/sev 확인, modprobe ccp, 그룹 권한 추가
게스트 부팅 중 triple fault OVMF 빌드가 SEV/TDX 미지원 AmdSev/IntelTdx 전용 OVMF 빌드 사용
"Not enough SEV ASIDs" SEV ASID 소진 실행 중인 SEV VM 수 확인, ASID 할당량 점검
SNP 게스트에서 #VC 무한 루프 GHCB 프로토콜 불일치 게스트/호스트 커널 버전 일치 확인, GHCB v2 호환성
"TDX module not loaded" P-SEAMLDR 미설치 또는 BIOS TDX 비활성화 BIOS에서 TDX 활성화, P-SEAMLDR/TDX Module 설치
TDX 게스트 메모리 accept 실패 CONFIG_UNACCEPTED_MEMORY=n 게스트 커널에 해당 옵션 활성화
SWIOTLB 슬롯 부족 bounce buffer 크기 부족 swiotlb=262144 (512MB)로 증가
증명 보고서 서명 검증 실패 VCEK 인증서 만료 또는 TCB 불일치 AMD KDS에서 최신 인증서 다운로드, 펌웨어 업데이트
기밀 VM에서 VFIO passthrough 실패 장치가 TDISP/SEV-TIO 미지원 bounce buffer 기반 virtio 사용, TEE I/O 지원 GPU 확인
KVM_CREATE_VM ENOTSUP kvm_amd.sev_snp=0 또는 커널 미지원 cat /sys/module/kvm_amd/parameters/sev_snp 확인

일반적인 오류 메시지 해석

# SEV-SNP 관련 커널 로그 메시지 해석

# "SEV: GHCB protocol version mismatch"
# 원인: 게스트/호스트 GHCB 프로토콜 버전 불일치
# 해결: 양쪽 커널을 동일 GHCB v2 지원 버전으로 업데이트

# "SEV-SNP: RMP violation at address 0x..."
# 원인: 호스트가 게스트 private 메모리에 접근 시도
# 해결: 드라이버 또는 QEMU의 잘못된 메모리 접근 확인

# "PVALIDATE failed: page already validated"
# 원인: 이미 검증된 페이지를 중복 검증
# 해결: PVALIDATE 전 RMP 상태 확인 로직 추가

# "TDX: SEAMCALL failed: TDX_OPERAND_BUSY"
# 원인: 다른 LP가 동일 TDX 자원을 사용 중
# 해결: 재시도 로직 추가 (경합 상태)

# "TDX: not enough free KeyIDs"
# 원인: HKID 풀 소진 (동시 TD 수 초과)
# 해결: 미사용 TD 정리 또는 BIOS에서 KeyID 수 조정

# "SWIOTLB: coherent allocation failed"
# 원인: bounce buffer 공간 부족
# 해결: swiotlb 커널 파라미터 증가

디버깅 도구

# SEV-SNP 디버깅
# PSP 펌웨어 버전
cat /sys/devices/virtual/misc/sev/fw_version

# SEV 관련 커널 로그
dmesg | grep -iE "sev|snp|ccp|psp"

# GHCB 관련 로그
dmesg | grep -i ghcb

# RMP 테이블 정보 (호스트)
cat /sys/kernel/mm/sev/rmp_base
cat /sys/kernel/mm/sev/rmp_end

# sevctl — SNP 플랫폼 정보
sevctl ok        # 플랫폼 상태 확인
sevctl show      # 상세 정보
sevctl export    # 인증서 체인 내보내기

# TDX 디버깅
dmesg | grep -i tdx
cat /sys/firmware/tdx/tdx_module_version

# CoCo 게스트 타입 확인 (범용)
cat /sys/devices/system/cpu/sev      # 1 = SEV 활성
cat /sys/devices/system/cpu/tdx      # 1 = TDX 활성

# SWIOTLB 상태
cat /sys/kernel/debug/swiotlb/io_tlb_used
cat /sys/kernel/debug/swiotlb/io_tlb_nslabs

종합 디버깅 체크리스트

기밀 VM 디버깅 순서:
  1. 하드웨어 지원 확인: dmesg | grep -iE "sev|tdx|cca" — CPU가 CoCo를 지원하는지 확인
  2. BIOS/펌웨어 설정: BIOS에서 SEV/TDX 활성화, PSP 펌웨어 버전 확인
  3. 커널 모듈: lsmod | grep -E "ccp|kvm_amd|kvm_intel" — 필요 모듈 로드 확인
  4. KVM 파라미터: cat /sys/module/kvm_amd/parameters/sev_snp — 활성화 확인
  5. OVMF 호환성: SEV/TDX 전용 빌드 사용 여부 확인
  6. QEMU 버전: QEMU 8.0+ (SEV-SNP), QEMU 8.2+ (TDX) 요구
  7. 게스트 커널: CONFIG_AMD_MEM_ENCRYPT/CONFIG_INTEL_TDX_GUEST 확인
  8. SWIOTLB: bounce buffer 크기 충분한지 cat /sys/kernel/debug/swiotlb/io_tlb_overflow 확인

기밀 컴퓨팅은 가상화, 보안, 메모리 관리, 암호화 등 다양한 커널 서브시스템과 연결됩니다. 아래 문서를 함께 읽으면 전체적인 이해가 깊어집니다.

더 읽을거리: