IOMMU (Input-Output Memory Management Unit)
IOMMU를 DMA 주소 변환과 디바이스 격리를 담당하는 핵심 보안 계층으로 심층 분석합니다. Intel VT-d, AMD-Vi, ARM SMMU의 구조 차이와 페이지 테이블 모델, DMA remapping/interrupt remapping 동작, ATS/PASID/SVA 기반 고급 공유 주소 공간, VFIO 가상화 환경의 격리 보장, 성능 저하를 줄이기 위한 TLB flush 전략, DMA fault 디버깅과 우회 경로(SWIOTLB)까지 고성능 장치와 멀티테넌트 환경에서 필요한 운영 포인트를 다룹니다.
핵심 요약
- IOMMU — 디바이스 DMA 주소를 물리 주소로 변환하고 접근 제어하는 하드웨어입니다.
- DMA Remapping — 디바이스의 DMA 주소를 가상화하여 메모리 보호와 격리를 제공합니다.
- IOMMU Domain — 디바이스 그룹이 공유하는 주소 공간과 페이지 테이블입니다.
- VFIO — IOMMU를 활용하여 가상 머신에 디바이스를 안전하게 할당합니다.
- PASID/SVA — CPU와 디바이스가 동일한 가상 주소 공간을 공유합니다.
단계별 이해
- IOMMU 필요성 — 디바이스가 임의 메모리에 접근하면 보안 위협이므로, IOMMU로 DMA 주소를 제한합니다.
악의적인 디바이스나 버그가 있는 드라이버로부터 메모리를 보호합니다.
- 주소 변환 — IOMMU는 디바이스가 사용하는 IOVA를 물리 주소로 변환합니다.
IOMMU 없으면 DMA 주소 = 물리 주소, IOMMU 있으면 DMA 주소 = IOVA (가상화)
- Domain과 Group — 디바이스를 Domain(주소 공간)에 할당하고, Group(격리 단위)으로 관리합니다.
VFIO는 Group 단위로 디바이스를 가상 머신에 할당합니다.
- 실전 활용 — KVM/QEMU 가상화에서 GPU, NVMe 등 물리 디바이스를 VM에 직접 할당(passthrough)할 때 IOMMU가 필수입니다.
이를 통해 VM이 거의 네이티브 성능으로 디바이스를 사용할 수 있습니다.
개요
IOMMU(Input-Output Memory Management Unit)는 CPU의 MMU와 유사하게 I/O 디바이스가 메모리에 접근할 때 주소 변환과 보호를 제공합니다.
주요 목적
- DMA Remapping — 디바이스가 사용하는 DMA 주소를 가상화하여 물리 메모리 보호
- Interrupt Remapping — 인터럽트 메시지 보안 강화 (interrupt injection 공격 방지)
- Device Isolation — 디바이스 간 메모리 격리로 보안 강화
- VM Device Passthrough — 가상 머신에 물리 디바이스 직접 할당
- SVA (Shared Virtual Addressing) — CPU와 디바이스가 동일한 가상 주소 공간 공유
하드웨어 구현
| 벤더 | 기술 | 설명 |
|---|---|---|
| Intel | VT-d (Virtualization Technology for Directed I/O) | DMA/인터럽트 remapping, PASID 지원 |
| AMD | AMD-Vi (I/O Virtualization) | IOMMU v2/v3, GCR3 테이블 |
| ARM | SMMU (System Memory Management Unit) | SMMUv2/v3, Stream ID 기반 매핑 |
IOMMU 아키텍처
주요 구성 요소
IOMMU Domain
IOMMU domain은 동일한 주소 공간을 공유하는 디바이스 그룹입니다.
struct iommu_domain {
unsigned int type; /* IOMMU_DOMAIN_* */
const struct iommu_ops *ops;
unsigned long pgsize_bitmap; /* 지원 페이지 크기 */
struct iommu_domain_geometry geometry;
void *iova_cookie; /* IOVA allocator */
struct iommu_iotlb_gather iotlb_gather;
};
/* Domain Types */
#define IOMMU_DOMAIN_BLOCKED 0 /* 모든 DMA 차단 */
#define IOMMU_DOMAIN_IDENTITY 1 /* 1:1 매핑 (passthrough) */
#define IOMMU_DOMAIN_UNMANAGED 2 /* 수동 관리 (VFIO) */
#define IOMMU_DOMAIN_DMA 3 /* DMA API 통합 */
#define IOMMU_DOMAIN_DMA_FQ 4 /* Flush Queue 최적화 */
IOMMU Group
IOMMU group은 격리 가능한 최소 디바이스 단위입니다. 같은 그룹의 디바이스는 서로를 구분할 수 없습니다.
struct iommu_group {
int id;
struct kobject kobj;
struct kobject *devices_kobj;
struct list_head devices; /* 그룹 내 디바이스 */
struct iommu_domain *domain;
void *iommu_data;
};
/* IOMMU Group 확인 */
# ls -l /sys/kernel/iommu_groups/
drwxr-xr-x 2 root root 0 Jan 1 00:00 0
drwxr-xr-x 2 root root 0 Jan 1 00:00 1
drwxr-xr-x 2 root root 0 Jan 1 00:00 2
# ls /sys/kernel/iommu_groups/0/devices/
0000:00:00.0 0000:00:00.1
IOMMU Operations
iommu_ops 구조체
struct iommu_ops {
bool (*capable)(enum iommu_cap);
struct iommu_domain *(*domain_alloc)(unsigned int type);
void (*domain_free)(struct iommu_domain *domain);
int (*attach_dev)(struct iommu_domain *domain,
struct device *dev);
void (*detach_dev)(struct iommu_domain *domain,
struct device *dev);
int (*map)(struct iommu_domain *domain,
unsigned long iova, phys_addr_t paddr,
size_t size, int prot, gfp_t gfp);
size_t (*unmap)(struct iommu_domain *domain,
unsigned long iova, size_t size,
struct iommu_iotlb_gather *gather);
phys_addr_t (*iova_to_phys)(struct iommu_domain *domain,
dma_addr_t iova);
struct iommu_device *(*probe_device)(struct device *dev);
void (*release_device)(struct device *dev);
void (*iotlb_sync)(struct iommu_domain *domain,
struct iommu_iotlb_gather *gather);
int (*enable_nesting)(struct iommu_domain *domain);
/* SVA (Shared Virtual Addressing) */
int (*sva_bind)(struct device *dev, struct mm_struct *mm,
void *drvdata);
void (*sva_unbind)(struct device *dev, struct mm_struct *mm);
/* Page Request Interface (PRI) */
int (*page_response)(struct device *dev,
struct iommu_fault_event *evt,
struct iommu_page_response *msg);
};
IOMMU 매핑
/* IOMMU domain 할당 */
struct iommu_domain *domain;
domain = iommu_domain_alloc(&pci_bus_type);
/* 디바이스를 domain에 연결 */
iommu_attach_device(domain, dev);
/* IOVA → Physical Address 매핑 */
unsigned long iova = 0x100000;
phys_addr_t paddr = virt_to_phys(buffer);
size_t size = PAGE_SIZE;
int prot = IOMMU_READ | IOMMU_WRITE;
iommu_map(domain, iova, paddr, size, prot);
/* IOVA로 물리 주소 조회 */
phys_addr_t phys = iommu_iova_to_phys(domain, iova);
/* 매핑 해제 */
iommu_unmap(domain, iova, size);
/* 디바이스 분리 및 domain 해제 */
iommu_detach_device(domain, dev);
iommu_domain_free(domain);
Intel VT-d
VT-d 데이터 구조
Intel VT-d는 계층적 페이지 테이블 구조를 사용합니다.
VT-d MMIO 레지스터
/* VT-d 레지스터 오프셋 */
#define DMAR_VER_REG 0x00 /* Version */
#define DMAR_CAP_REG 0x08 /* Capability */
#define DMAR_ECAP_REG 0x10 /* Extended Capability */
#define DMAR_GCMD_REG 0x18 /* Global Command */
#define DMAR_GSTS_REG 0x1c /* Global Status */
#define DMAR_RTADDR_REG 0x20 /* Root Table Address */
#define DMAR_CCMD_REG 0x28 /* Context Command */
#define DMAR_IVA_REG 0x50 /* Invalidate Address */
#define DMAR_IOTLB_REG 0x58 /* IOTLB Invalidate */
#define DMAR_IRTA_REG 0xb8 /* Interrupt Remapping Table */
/* Capability 비트 */
#define DMA_CAP_SAGAW ((1 << 8) - 1) /* Supported AGW */
#define DMA_CAP_CM (1 << 7) /* Caching Mode */
#define DMA_CAP_PI (1 << 59) /* Posted Interrupts */
DMAR (DMA Remapping)
ACPI DMAR 테이블은 VT-d 하드웨어 유닛을 기술합니다.
/* ACPI DMAR 테이블 파싱 */
# dmesg | grep -i dmar
DMAR: Host address width 39
DMAR: DRHD base: 0x00000000fed90000 flags: 0x1
DMAR: RMRR base: 0x0000000079800000 end: 0x000000007affffff
DMAR: IOMMU enabled
/* 커널 파라미터 */
intel_iommu=on # VT-d 활성화
intel_iommu=on,sm_on # Scalable Mode 활성화
intel_iommu=off # VT-d 비활성화
iommu=pt # Pass-through 모드 (성능 우선)
Interrupt Remapping
Interrupt remapping은 MSI/MSI-X 인터럽트를 IOMMU가 가로채서 재매핑합니다. 이는 interrupt injection 공격을 방지합니다.
Interrupt Remapping Table Entry (IRTE)
struct irte {
u64 present : 1;
u64 fpd : 1; /* Fault Processing Disable */
u64 dst_mode : 1; /* Destination Mode (physical/logical) */
u64 redir_hint : 1; /* Redirection Hint */
u64 trigger_mode : 1; /* Trigger Mode (edge/level) */
u64 dlvry_mode : 3; /* Delivery Mode */
u64 avail : 4;
u64 reserved : 3;
u64 irte_mode : 1; /* 0=Remapped, 1=Posted */
u64 vector : 8; /* Interrupt Vector */
u64 reserved2 : 8;
u64 dest_id : 32; /* Destination APIC ID */
};
/* Interrupt remapping 활성화 */
# dmesg | grep -i "interrupt remapping"
DMAR-IR: Enabled IRQ remapping in x2apic mode
Posted Interrupts
Posted Interrupts는 가상화 환경에서 인터럽트를 VM에 직접 전달하여 VM-exit를 줄입니다.
struct pi_desc {
u32 pir[8]; /* Posted Interrupt Requests (256-bit) */
u32 control; /* ON, SN, NV */
u32 rsvd[7];
} __aligned(64);
/* VT-d Posted Interrupt Descriptor Address */
#define IRTE_PI_ADDR_MASK 0xfffffffffff00000
PASID (Process Address Space ID)
PASID는 단일 디바이스가 여러 주소 공간(프로세스)을 구분할 수 있게 합니다.
PASID Table
struct pasid_entry {
u64 present : 1;
u64 pwt : 1; /* Page-level Write-Through */
u64 pcd : 1; /* Page-level Cache Disable */
u64 reserved : 9;
u64 slptptr : 52; /* Second Level Page Table Pointer */
u64 pgtt : 3; /* Page Table Type */
u64 reserved2 : 61;
};
/* PASID 할당 */
int pasid;
pasid = iommu_sva_alloc_pasid(dev, 1, (1 << 20) - 1);
SVA (Shared Virtual Addressing)
SVA는 CPU와 디바이스가 동일한 가상 주소 공간을 공유합니다. CPU의 페이지 테이블을 IOMMU가 직접 사용합니다.
#include <linux/iommu.h>
/* SVA 바인딩 */
struct iommu_sva *sva;
sva = iommu_sva_bind_device(dev, current->mm, NULL);
if (IS_ERR(sva))
return PTR_ERR(sva);
/* PASID 획득 */
int pasid = iommu_sva_get_pasid(sva);
/* 디바이스는 이제 user space 가상 주소 직접 사용 가능 */
void *user_buf = mmap(...);
device_submit_command(user_buf, pasid); /* CPU와 동일 VA */
/* SVA 언바인딩 */
iommu_sva_unbind_device(sva);
VFIO (Virtual Function I/O)
VFIO는 IOMMU를 활용하여 userspace에서 안전하게 디바이스를 제어할 수 있게 합니다.
VFIO Container & Group
/* VFIO 사용 예시 (QEMU/KVM) */
# modprobe vfio-pci
# PCI 디바이스를 vfio-pci로 바인딩
# echo 0000:01:00.0 > /sys/bus/pci/devices/0000\:01\:00.0/driver/unbind
# echo 8086 10fb > /sys/bus/pci/drivers/vfio-pci/new_id
# IOMMU group 확인
# readlink /sys/bus/pci/devices/0000:01:00.0/iommu_group
../../../kernel/iommu_groups/1
# QEMU에서 디바이스 패스스루
qemu-system-x86_64 \
-device vfio-pci,host=01:00.0 \
...
VFIO API
#include <linux/vfio.h>
/* VFIO container 열기 */
int container = open("/dev/vfio/vfio", O_RDWR);
/* IOMMU group 열기 */
int group = open("/dev/vfio/1", O_RDWR);
/* Group을 container에 추가 */
ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);
/* IOMMU type 설정 (Type1 = DMA remapping) */
ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
/* DMA 매핑 */
struct vfio_iommu_type1_dma_map dma_map = {
.argsz = sizeof(dma_map),
.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
.vaddr = (__u64)buffer,
.iova = 0x100000,
.size = 4096,
};
ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);
ARM SMMU
ARM System Memory Management Unit는 ARM 플랫폼의 IOMMU 구현입니다.
SMMU 아키텍처
SMMU 버전
| 버전 | 특징 |
|---|---|
| SMMUv2 | Stream ID (SID) 기반, Context Bank, Stage 1/2 translation |
| SMMUv3 | 향상된 확장성, Command Queue, Event Queue, PRI/ATS 지원 |
| SMMUv3.1 | Sub-streams, HTTU (Hardware Translation Table Update) |
SMMU 드라이버
/* Device Tree에서 SMMU 정의 */
smmu: iommu@2b400000 {
compatible = "arm,smmu-v3";
reg = <0x0 0x2b400000 0x0 0x100000>;
interrupts = <GIC_SPI 74 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 75 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 76 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "eventq", "priq", "gerror";
#iommu-cells = <1>;
};
/* 디바이스가 SMMU 사용 */
pcie@40000000 {
iommus = <&smmu 0>; /* Stream ID = 0 */
};
디버깅
sysfs 인터페이스
# IOMMU 정보 확인
# cat /sys/kernel/iommu_groups/0/type
intel-iommu
# ls /sys/kernel/iommu_groups/0/devices/
0000:00:00.0
# 디바이스별 IOMMU group
# find /sys/kernel/iommu_groups/ -type l | sort -V
/sys/kernel/iommu_groups/0/devices/0000:00:00.0
/sys/kernel/iommu_groups/1/devices/0000:00:02.0
커널 로그
# IOMMU 초기화 로그
# dmesg | grep -i iommu
DMAR: IOMMU enabled
DMAR-IR: Enabled IRQ remapping in x2apic mode
iommu: Default domain type: Translated
# IOMMU fault
DMAR: [DMA Read] Request device [01:00.0] fault addr 0xdeadbeef
DMAR: fault reason 0x06 (PTE Read access is not set)
Tracing
# IOMMU tracepoints
# cd /sys/kernel/debug/tracing
# echo 1 > events/iommu/enable
# 주요 tracepoint
iommu:map # IOVA 매핑
iommu:unmap # IOVA 언매핑
iommu:attach_device_to_domain
iommu:detach_device_from_domain
# cat trace
swapper/0-1 [000] .... 1.234: iommu_map: IOMMU:0000:00:00.0 iova=0x100000 paddr=0x50000000 size=0x1000
성능 고려사항
IOTLB (I/O TLB) 최적화
/* IOTLB 무효화 지연 (Flush Queue) */
#define IOMMU_DOMAIN_DMA_FQ 4 /* Flush Queue 사용 */
/* Strict mode vs Flush Queue */
iommu.strict=0 # Flush Queue 사용 (성능 우선)
iommu.strict=1 # Strict mode (보안 우선, unmap 즉시 IOTLB flush)
Pass-through 모드
/* Pass-through: IOMMU 우회 (1:1 매핑) */
iommu=pt # 기본 디바이스는 pass-through
/* 성능 비교 */
/* Pass-through: DMA latency 최소 */
/* Translation: 보안 강화, VM passthrough 필요 */
커널 설정
CONFIG_IOMMU_SUPPORT=y
CONFIG_IOMMU_API=y
# Intel VT-d
CONFIG_INTEL_IOMMU=y
CONFIG_INTEL_IOMMU_DEFAULT_ON=y
CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON=y
CONFIG_INTEL_IOMMU_SVM=y # SVA 지원
# AMD-Vi
CONFIG_AMD_IOMMU=y
CONFIG_AMD_IOMMU_V2=y
# ARM SMMU
CONFIG_ARM_SMMU=y
CONFIG_ARM_SMMU_V3=y
CONFIG_ARM_SMMU_V3_SVA=y
# VFIO
CONFIG_VFIO=y
CONFIG_VFIO_PCI=y
CONFIG_VFIO_IOMMU_TYPE1=y
# 디버깅
CONFIG_IOMMU_DEBUG=y
CONFIG_IOMMU_DEBUGFS=y
참고자료
- Linux IOMMU Documentation
- Intel VT-d Specification
- ARM SMMU Architecture Specification
drivers/iommu/intel/iommu.c— Intel IOMMU 드라이버drivers/iommu/amd/iommu.c— AMD IOMMU 드라이버drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c— ARM SMMUv3 드라이버drivers/vfio/vfio_iommu_type1.c— VFIO Type1 IOMMU
- DMA — IOMMU와 DMA API 통합
- PCI/PCIe — PCI 디바이스 주소 지정
- 가상화 (KVM) — VFIO 디바이스 패스스루
- 메모리 관리 (심화) — 페이지 테이블 구조
관련 문서
- NAT (Network Address Translation) — nf_nat 아키텍처, SNAT/DNAT/MASQUERADE, conntrack 연동, n
- 디바이스 드라이버 (Device Drivers) — Linux Device Model, platform driver, deferred prob
- DMA Engine (DMA 컨트롤러 프레임워크) — DMA Engine 프레임워크, dma_device/dma_chan 구조체, Provide