Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX BELLEK KORUMASI 2026

IOMMU / SMMU
DMA İzolasyonu ve Bellek Koruması

ARM SMMUv2/v3 ile DMA izolasyonu, IOMMU grubu yönetimi, VFIO temelleri ve gömülü güvenli sistem tasarımı.

00 IOMMU neden gerekli — tehdit modeli

DMA (Direct Memory Access) yetenekli her cihaz, teorik olarak tüm fiziksel belleğe yazabilir. IOMMU bu yetkiyi sınırlandırarak sistem güvenliğinin temel katmanlarından birini oluşturur.

DMA Saldırı Yüzeyi

DMA mekanizması, cihazların CPU'yu devre dışı bırakarak belleğe doğrudan erişmesini sağlar. Bu özellik performans için zorunludur; ancak kötü niyetli veya hatalı bir cihaz, DMA üzerinden aşağıdaki saldırıları gerçekleştirebilir:

Rastgele Bellek Okuma Cihaz, ayrılan DMA tampon dışındaki adresleri okuyarak kernel veri yapılarına, şifreleme anahtarlarına veya diğer süreçlerin belleğine erişebilir.
Rastgele Bellek Yazma Cihaz, sayfa tablolarını veya kernel kodunu yazarak ayrıcalık yükseltme (privilege escalation) saldırısı gerçekleştirebilir.
PCIe Thunderbolt Saldırısı Fiziksel erişime sahip saldırgan, Thunderbolt veya ExpressCard üzerinden kötü niyetli donanım bağlayarak sistemi ele geçirebilir. IOMMU olmayan sistemlerde bu saldırı saniyeler içinde gerçekleşebilir.
AXI Bus Güvenlik Açıkları ARM SoC'larda AXI fabric üzerindeki bir IP bloğunun ele geçirilmesi, SMMU yoksa tüm bellek haritasına erişim sağlar. Gömülü güvenlik uygulamaları için kritik bir tehdit vektörüdür.

IOMMU Nasıl Korur?

IOMMU (Input-Output Memory Management Unit), CPU'nun MMU'sunun (Memory Management Unit) I/O cihazları için olan karşılığıdır. Her cihazın erişebildiği bellek aralığını tanımlayan bir sayfa tablosu tutar. Cihaz, eşlenmemiş bir adrese erişmeye çalışırsa IOMMU işlemi engeller ve bir hata (fault) üretir.

Cihaz (DMA Master)
    |
    | bus adresi (IOVA)
    v
 IOMMU / SMMU
    |--- Adres eşleme tablosuna bak ---+
    |                                  |
    | İzin Var                         | İzin Yok
    v                                  v
Fiziksel Bellek             Context Fault (IRQ) -> Kernel Handler
(erişim gerçekleşir)        (erişim engellendi, hata raporu)

IOVA ve Fiziksel Adres Farkı

Cihaz, fiziksel adresleri değil IOVA (I/O Virtual Address) kullanır. IOMMU bu IOVA'ları fiziksel adreslere çevirir. Böylece aynı fiziksel adresin farklı cihazlara farklı IOVA'larla görünmesi mümkündür; bu da izolasyonun temelidir.

SenaryoIOMMU YokIOMMU Var
Cihaz DMA adresiFiziksel adresIOVA (sanal)
Bellek erişim sınırıTüm fiziksel bellekEşlenmiş bölgeler
Saldırı yüzeyiTüm bellekSadece izin verilen aralık
Hata tespitiYok (sessiz bozulma)Context fault + IRQ
VM geçişi (VFIO)Mümkün değil (güvensiz)Mümkün (izole edilmiş)

01 ARM SMMU mimarisi — SMMUv2 ve SMMUv3

ARM'ın SMMU (System Memory Management Unit) standardı, IOMMU işlevini ARM SoC'ları için tanımlar. SMMUv2 ve SMMUv3 arasında mimari açıdan önemli farklılıklar bulunur.

SMMUv2 Mimarisi

SMMUv2 (ARM IHI 0062), Cortex-A53/A57 nesli SoC'larda yaygın kullanılan ilk büyük SMMU revizyonudur. StreamID tabanlı cihaz kimliği kullanır ve iki aşamalı adres çevirisi destekler.

StreamID Her cihazın benzersiz kimliği. PCIe'de BDF (Bus:Device:Function) kodundan, AMBA AXI fabric'te ise bus master ID'den türetilir. Aynı SMMU'ya bağlı farklı cihazlar farklı StreamID'ye sahiptir.
Stream Match Register (SMR) StreamID'yi bir Context Bank'a eşleyen kayıt. Maske destekler; bir SMR birden fazla StreamID'yi aynı context'e yönlendirebilir.
Context Bank Her izolasyon domaininin ayrı sayfa tablosu ve TTBR (Translation Table Base Register) bulunur. SMMUv2'de maksimum 128 context bank desteği.
Stage-1 Çevirisi IOVA'dan IPA'ya (Intermediate Physical Address) çeviri. Genellikle OS tarafından yönetilir.
Stage-2 Çevirisi IPA'dan PA'ya (Physical Address) çeviri. Hypervisor (EL2) tarafından yönetilir. Sanallaştırma senaryolarında kullanılır.

SMMUv3 Mimarisi

SMMUv3 (ARM IHI 0070), Cortex-A55/A76 nesli ile birlikte gelir. Komut kuyruğu tabanlı (command queue) bir arayüzle önceki sürümden temelden farklılaşır ve ölçeklenebilirliği büyük ölçüde artırır.

ÖzellikSMMUv2SMMUv3
Cihaz kimliğiStreamID (16 bit)StreamID (32 bit) + SubstreamID (20 bit)
Context erişimiDoğrudan register yazmaKomut kuyruğu (CMD_QUEUE)
Hata raporlamaGlobal fault registerOlay kuyruğu (EVENT_QUEUE)
TLB geçersizleştirmeSenkron, globalAsenkron, granüler (TLBI_NH_ASID vb.)
Sayfa boyutu desteği4KB, 64KB4KB, 16KB, 64KB, 1GB süper-sayfa
PCIe ATSYokVar (Address Translation Services)
PRI (Page Request Interface)YokVar
SVA (Shared Virtual Addressing)YokVar (PasID ile)

İki Aşamalı Çeviri Akışı

Cihaz DMA talebi (IOVA)
    |
    v
SMMU Stage-1 (OS kontrolünde)
  IOVA --[STE/CD tablosu]--> IPA
    |
    v
SMMU Stage-2 (Hypervisor kontrolünde)
  IPA  --[S2 sayfa tablosu]--> PA
    |
    v
Fiziksel bellek erişimi

02 Linux IOMMU çerçevesi

Linux kernel, farklı IOMMU donanımlarını (ARM SMMU, Intel VT-d, AMD-Vi) tek bir soyut arayüz altında toplar. Bu arayüz sürücü geliştiricilerine donanımdan bağımsız bir API sunar.

Temel Veri Yapıları

/* IOMMU domain — bir izolasyon alanı */
struct iommu_domain {
    unsigned type;        /* IOMMU_DOMAIN_DMA, IDENTITY, UNMANAGED */
    const struct iommu_ops *ops;  /* Donanıma özgü işlemler */
    unsigned long pgsize_bitmap;  /* Desteklenen sayfa boyutları */
    iommu_fault_handler_t handler; /* Context fault handler */
    void *handler_token;
    struct iommu_domain_geometry geometry; /* IOVA aralığı */
    struct iommu_dma_cookie *iova_cookie;  /* IOVA havuzu */
};

/* IOMMU grubu — aynı domaine bağlı cihaz kümesi */
struct iommu_group {
    struct kobject kobj;
    struct kobject *devices_kobj;
    struct list_head devices;        /* Gruptaki cihazlar */
    struct mutex mutex;
    struct blocking_notifier_head notifier;
    void *iommu_data;
    void (*iommu_data_release)(void *iommu_data);
    char *name;
    int id;
    struct iommu_domain *default_domain; /* Varsayılan domain */
    struct iommu_domain *blocking_domain;
};

iommu_ops Arayüzü

Her IOMMU sürücüsü iommu_ops yapısını uygular. Bu yapı, sayfa tablosu yönetiminden TLB geçersizleştirmeye kadar tüm donanım işlemlerini tanımlar:

struct iommu_ops {
    bool (*capable)(struct device *dev, enum iommu_cap);
    struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type);
    void (*domain_free)(struct iommu_domain *);
    int (*attach_dev)(struct iommu_domain *, struct device *);
    void (*detach_dev)(struct iommu_domain *, struct device *);
    int (*map)(struct iommu_domain *, unsigned long iova,
               phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
    size_t (*unmap)(struct iommu_domain *, unsigned long iova,
                    size_t size, struct iommu_iotlb_gather *);
    phys_addr_t (*iova_to_phys)(struct iommu_domain *, dma_addr_t iova);
    struct iommu_group *(*device_group)(struct device *);
    void (*get_resv_regions)(struct device *, struct list_head *);
    int (*of_xlate)(struct device *, struct of_phandle_args *);
    /* ... */
};

DMA-API ile Entegrasyon

Sürücüler genellikle doğrudan IOMMU API'sini çağırmaz; bunun yerine dma_map_single, dma_alloc_coherent gibi DMA-API fonksiyonlarını kullanır. IOMMU, bu fonksiyonların altında şeffaf olarak çalışır:

/* IOMMU etkinken DMA-API akışı */
dma_map_single(dev, cpu_ptr, size, DMA_FROM_DEVICE)
    |
    v
iommu_map(domain, iova, phys, size, IOMMU_READ | IOMMU_WRITE)
    |
    v
ARM SMMU: arm_smmu_map() -> sayfa tablosuna giriş ekle
    |
    v
dma_addr_t (IOVA) sürücüye döner
    |
    v
Sürücü bu adresi donanıma yazar (DMA başlar)

03 Device Tree ile SMMU yapılandırması

ARM platformlarında SMMU, Device Tree (DT) aracılığıyla yapılandırılır. Cihaz ile SMMU arasındaki ilişki DT bağlantılarıyla tanımlanır; Linux IOMMU altyapısı bu bağlantıları önyükleme sırasında işler.

SMMU Node Tanımı

/* ARM64 DT örneği: SMMUv2 tanımı */
smmu_0: iommu@fd800000 {
    compatible = "arm,mmu-500";
    reg = <&smmu_reg 0 0 0 0x20000>;
    interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
    interrupt-names = "global-faults", "perf-counters",
                      "context-faults";
    #iommu-cells = <1>;     /* 1 = StreamID sayısı */
    #global-interrupts = <2>;

    /* SMMU'nun bağlı olduğu bellek bölgeleri */
    dma-ranges;
};

Cihaz SMMU Bağlantısı

/* Ethernet MAC — SMMU'ya StreamID 0x42 ile bağlı */
ethernet@ff000000 {
    compatible = "vendor,my-eth";
    reg = <0xff000000 0x10000>;
    interrupts = <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clk_eth>;

    /* SMMU bağlantısı: iommu referansı + StreamID */
    iommus = <&smmu_0 0x42>;

    /* IOMMU grubu ayrımı için gerekirse */
    iommu-map = <0x0 &smmu_0 0x42 0x1>;
};

/* Video DMA — farklı StreamID */
video_dma@fe000000 {
    compatible = "vendor,my-vdma";
    reg = <0xfe000000 0x8000>;
    iommus = <&smmu_0 0x55>;  /* StreamID 0x55 */
};

SMMUv3 DT Tanımı

/* SMMUv3 (daha yeni platformlar) */
smmu_1: iommu@09050000 {
    compatible = "arm,smmu-v3";
    reg = <0x0 0x09050000 0x0 0x80000>;
    interrupts = <GIC_SPI 74 IRQ_TYPE_EDGE_RISING>,
                 <GIC_SPI 75 IRQ_TYPE_EDGE_RISING>,
                 <GIC_SPI 76 IRQ_TYPE_EDGE_RISING>,
                 <GIC_SPI 77 IRQ_TYPE_EDGE_RISING>;
    interrupt-names = "eventq", "priq", "cmdq-sync", "gerror";
    #iommu-cells = <1>;
    dma-coherent;      /* Donanım cache coherency desteği */
    /* StreamID genişliği: 16 bit */
    arm,sid-bits = <16>;
};

Önyükleme Sırasında DT İşleme

Kernel onyukleme
    |
    v
of_platform_populate() -- DT node'larini tara
    |
    v
iommu node bulundu (compatible = "arm,mmu-500")
    -> arm_smmu_probe() cagrılır
    -> SMMU donanimi baslatilir
    |
    v
Cihaz node'u islenirken "iommus" property bulundu
    -> of_iommu_xlate() cagrılır
    -> iommu_group olusturulur veya var olana eklenir
    |
    v
Cihaz surucu probe() cagrildiginda:
    -> iommu_attach_device() ile domain'e baglanir

İlgili Kernel Parametreleri

iommu.passthrough=1 Tüm cihazlar için IOMMU'yu identity (passthrough) moduna alır. Test ve geliştirme amaçlıdır; üretimde kullanılmamalıdır.
iommu.strict=1 TLB geçersizleştirme işlemlerini senkron olarak gerçekleştirir. Performans kaybına yol açar ama güvenli bellek serbest bırakma garantisi verir.
arm-smmu.disable_bypass=1 StreamID olmayan cihazların (SMMU'ya bağlanmamış) bypass etmesini engeller. Bu cihazlar sıfır erişim izniyle reddedilir.

04 IOMMU domain tipleri

Linux IOMMU çerçevesi birden fazla domain tipi tanımlar. Hangi tipin kullanılacağı, cihazın güvenlik gereksinimlerine ve çalışma moduna göre belirlenir.

Domain Tipleri

IOMMU_DOMAIN_DMA Standart sürücü kullanımı için. DMA-API fonksiyonları bu domain tipiyle çalışır. IOVA tahsisi otomatik olarak yönetilir. Linux'ta varsayılan moddur.
IOMMU_DOMAIN_IDENTITY Passthrough modu: IOVA = PA. Cihaz, fiziksel adresle doğrudan çalışır; IOMMU adres çevirisi yapmaz ama erişim denetimi yapabilir. Eski sürücüler için geçiş modu.
IOMMU_DOMAIN_UNMANAGED Manuel domain yönetimi. Kullanıcı (veya VFIO gibi alt sistem) eşlemeleri elle kontrol eder. VM geçişi ve özel güvenlik senaryoları için kullanılır.
IOMMU_DOMAIN_BLOCKED Tüm erişimler engellenir. Güvenli varsayılan: cihaz bir domain'e eklenmeden önce bu modda kalır.

Domain Oluşturma ve Cihaz Bağlama

#include <linux/iommu.h>

/* Yeni bir DMA domain oluştur */
struct iommu_domain *domain = iommu_domain_alloc(&platform_bus_type);
if (!domain) {
    pr_err("IOMMU domain oluşturulamadı\n");
    return -ENOMEM;
}

/* Cihazı domain'e bağla */
int ret = iommu_attach_device(domain, dev);
if (ret) {
    pr_err("Cihaz domain'e baglanamadi: %d\n", ret);
    iommu_domain_free(domain);
    return ret;
}

/* Bir bellek bölgesi eşle */
ret = iommu_map(domain,
    iova,       /* I/O sanal adres */
    paddr,      /* Fiziksel adres */
    size,       /* Boyut (sayfa katı) */
    IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE,
    GFP_KERNEL
);

/* Eşlemeyi kaldır */
size_t unmapped = iommu_unmap(domain, iova, size);

/* Cihazı ayır */
iommu_detach_device(domain, dev);
iommu_domain_free(domain);

IOMMU Grup Yönetimi

Bir IOMMU grubu, aynı izolasyon birimine dahil edilen cihazların kümesidir. Genellikle aynı PCIe Root Port veya AXI fabric segment'ini paylaşan cihazlar aynı grupta yer alır. Tüm grup aynı domain'e bağlanmak zorundadır:

/* Cihazın grubunu al */
struct iommu_group *group = iommu_group_get(dev);
if (!group) {
    pr_err("Cihazın IOMMU grubu yok\n");
    return -ENODEV;
}

/* Gruptaki cihaz sayısı */
int count = iommu_group_get_iommudata(group);

/* Grup ID'sini öğren */
int gid = iommu_group_id(group);
pr_info("Cihaz IOMMU grup %d'de\n", gid);

/* Referansı serbest bırak */
iommu_group_put(group);

# Kullanıcı alanından grup bilgisi:
ls /sys/bus/platform/devices/ff000000.ethernet/iommu_group/
# devices/  type  reserved_regions

Domain Ayırma Kontrolü

# Bir cihazın hangi domain'e bağlı olduğunu kontrol et:
cat /sys/bus/platform/devices/ff000000.ethernet/iommu_group/type
# DMA  (veya identity, unmanaged)

# Tüm IOMMU gruplarını listele:
ls /sys/kernel/iommu_groups/

# Grup 5'in üyelerini göster:
ls /sys/kernel/iommu_groups/5/devices/

05 DMA güvenli tahsis ve IOMMU eşleme

IOMMU etkin bir sistemde DMA tamponları tahsis ederken ve fiziksel adresleri eşlerken özel dikkat gerektirir. Yanlış kullanım hem güvenlik açığı hem de bellek bozulmasına neden olabilir.

dma_alloc_coherent ile Tutarlı Bellek

Tutarlı (coherent) DMA belleği, hem CPU hem cihaz tarafından tutarlı şekilde görülen, önbellek senkronizasyonu gerektirmeyen bir bölgedir. IOMMU etkinken bu belleğin IOVA alanına eşlenmesi otomatik gerçekleşir:

#include <linux/dma-mapping.h>

/* 4 KB tutarlı DMA belleği tahsis et */
dma_addr_t dma_handle;
void *cpu_addr = dma_alloc_coherent(dev, 4096,
                                    &dma_handle, GFP_KERNEL);
if (!cpu_addr) {
    dev_err(dev, "DMA bellek tahsis hatası\n");
    return -ENOMEM;
}

/* cpu_addr: CPU'nun eriştiği sanal adres */
/* dma_handle: Cihaza yazılacak IOVA değeri */

/* Cihaza IOVA adresini bildir */
writel((u32)dma_handle, dev_base + DMA_ADDR_REG);
writel(4096,            dev_base + DMA_LEN_REG);
writel(DMA_START,       dev_base + DMA_CTRL_REG);

/* DMA tamamlandıktan sonra serbest bırak */
dma_free_coherent(dev, 4096, cpu_addr, dma_handle);

Scatter-Gather ile IOMMU Eşleme

Büyük veya parçalı tamponlar için scatter-gather listesi kullanılır. IOMMU, bu parçaları cihazdan tek parçaymış gibi görünen ardışık bir IOVA aralığına eşleyebilir (IOMMU merging):

#include <linux/scatterlist.h>

struct scatterlist sg[4];
struct page *pages[4];
int i;

/* Sayfaları tahsis et (bitişik olmak zorunda değil) */
for (i = 0; i < 4; i++) {
    pages[i] = alloc_page(GFP_KERNEL);
    sg_set_page(&sg[i], pages[i], PAGE_SIZE, 0);
}
sg_mark_end(&sg[3]);

/* IOMMU üzerinden eşle */
int nents = dma_map_sg(dev, sg, 4, DMA_FROM_DEVICE);
if (nents == 0) {
    dev_err(dev, "SG eşleme başarısız\n");
    goto free_pages;
}

/* IOMMU birleştirme sonrası nents, 4'ten az olabilir */
pr_info("SG: %d giriş, %d eşleme sonrası\n", 4, nents);

for (i = 0; i < nents; i++) {
    pr_info("  sg[%d]: dma_addr=0x%llx len=%u\n",
            i, (u64)sg_dma_address(&sg[i]),
               sg_dma_len(&sg[i]));
}

/* İşlem sonrası eşlemeyi kaldır */
dma_unmap_sg(dev, sg, 4, DMA_FROM_DEVICE);

iommu_map ve iommu_unmap (Manuel)

VFIO veya özel güvenlik alt sistemleri gibi durumlarda eşlemeleri doğrudan IOMMU API üzerinden yönetmek gerekebilir:

/* Manuel eşleme — IOMMU_DOMAIN_UNMANAGED domain için */
ret = iommu_map(domain,
    0x40000000UL,    /* IOVA: cihazın göreceği adres */
    phys_addr,       /* PA: gerçek fiziksel adres */
    SZ_2M,           /* 2 MB eşle */
    IOMMU_READ | IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO,
    GFP_KERNEL
);

/* Eşlemeyi doğrula */
phys_addr_t check = iommu_iova_to_phys(domain, 0x40000000UL);
pr_info("IOVA 0x40000000 -> PA 0x%llx\n", (u64)check);

/* Eşlemeyi kaldır */
iommu_unmap(domain, 0x40000000UL, SZ_2M);

Streaming vs Coherent DMA Karşılaştırması

Özellikdma_alloc_coherentdma_map_single (streaming)
Önbellek yönetimiOtomatik (non-cacheable)Açık sync gerektirir
Kullanım durumuSürekli erişim, kontrol yapılarıTek seferlik veri transferi
Bellek verimliliğiDaha az verimli (non-cached)Daha verimli
IOMMU eşleme ömrüKalıcı (free'ye kadar)Transfer süresi kadar

06 VFIO ve SMMU — cihaz geçişi

VFIO (Virtual Function I/O), kullanıcı alanı sürücülerine ve sanal makinelere gerçek donanım cihazlarını güvenli biçimde geçirmek için IOMMU altyapısını kullanır.

VFIO Mimarisi

VFIO üç temel kavram üzerine inşa edilmiştir: container, group ve device. Bu üçlü katmanlı yapı, izolasyon garantisini IOMMU ile sağlar.

Kullanici alani (VM / QEMU / uygulama)
    |
    | /dev/vfio/vfio (container)
    v
VFIO Container
    |--- IOMMU domain yönetimi
    |--- IOVA adres alanı kontrol
    |
    +--> VFIO Group (/dev/vfio/5)
         |--- Aynı IOMMU grubundaki cihazlar
         |
         +--> VFIO Device (bellek/irq/config erişimi)
              |--- BAR eşleme (mmap)
              |--- IRQ yönlendirme (eventfd)
              |--- Config space erişimi

VFIO ile Cihaz Kullanımı (Kullanıcı Alanı)

#include <linux/vfio.h>
#include <sys/ioctl.h>
#include <fcntl.h>

/* 1. Container aç */
int container = open("/dev/vfio/vfio", O_RDWR);

/* 2. IOMMU tipi ayarla (TYPE1 = standart Linux IOMMU) */
ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);

/* 3. Grubu aç */
int group = open("/dev/vfio/5", O_RDWR);  /* Grup 5 */

/* 4. Grubu container'a bağla */
ioctl(group, VFIO_GROUP_SET_CONTAINER, &container);

/* 5. Cihazı al */
int device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD,
                   "ff000000.ethernet");

/* 6. Cihaz bilgisini oku */
struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) };
ioctl(device, VFIO_DEVICE_GET_INFO, &dev_info);

/* 7. Konuk VM belleğini IOMMU'ya eşle */
struct vfio_iommu_type1_dma_map dma_map = {
    .argsz = sizeof(dma_map),
    .flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE,
    .vaddr = (uintptr_t)guest_mem,   /* VM bellek tamponu */
    .iova  = 0x80000000ULL,          /* IOVA adresi */
    .size  = guest_mem_size,
};
ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map);

VFIO-Platform (Gömülü Sistemler için)

PCIe olmayan gömülü MMIO cihazları için VFIO-Platform sürücüsü kullanılır. Platform cihazını VFIO'ya bağlamak için önce mevcut sürücüyü ayırıp vfio-platform'a bağlamak gerekir:

# Cihazın mevcut sürücüsünü ayır:
echo ff000000.ethernet > /sys/bus/platform/drivers/vendor-eth/unbind

# VFIO-platform sürücüsüne bağla:
echo ff000000.ethernet > /sys/bus/platform/drivers/vfio-platform/bind

# IOMMU grubunu kontrol et:
ls /sys/bus/platform/devices/ff000000.ethernet/iommu_group/devices/
# Yalnızca tek cihaz varsa güvenli geçiş yapılabilir

# /dev/vfio/ altında grup dosyasının oluştuğunu doğrula:
ls /dev/vfio/

QEMU ile VFIO Cihaz Geçişi

# QEMU komut satırı — ARM64 VM'e ethernet cihazı geçişi:
qemu-system-aarch64 \
    -M virt \
    -cpu cortex-a57 \
    -m 512M \
    -kernel Image \
    -device vfio-platform,host=ff000000.ethernet \
    -nographic

# VM içindeki sürücü cihazı doğrudan donanıma erişir
# SMMU, VM bellek eşlemesini korur

07 Hata ayıklama ve context fault analizi

IOMMU hataları, sürücü geliştirmede karşılaşılan en yaygın ve yanıltıcı sorunlardan biridir. Hata mesajlarını doğru yorumlamak ve debug altyapısını etkin kullanmak gereklidir.

CONFIG_IOMMU_DEBUG

# Derleme zamanı debug seçenekleri:
CONFIG_IOMMU_DEBUG=y
CONFIG_IOMMU_DEFAULT_DMA_STRICT=y  # Senkron TLB geçersizleştirme
CONFIG_ARM_SMMU_DEBUG=y            # ARM SMMU özel debug
CONFIG_IOVA_STATISTICS=y           # IOVA istatistikleri

# Çalışma zamanı debug etkinleştirme (kernel parametresi):
iommu.debug=1

# DebugFS üzerinden IOMMU durumu:
mount -t debugfs none /sys/kernel/debug
ls /sys/kernel/debug/iommu/

Context Fault Handler

SMMU, yetkisiz bir bellek erişimi tespit ettiğinde bir context fault IRQ üretir. Linux bu IRQ'yu yakalar ve kernel log'una ayrıntılı bilgi yazar:

/* Kernel log'da context fault örneği */
/* arm-smmu 09050000.iommu: Unhandled context fault:
 *   iova=0x40100000, fsr=0x402 (TF|PF), fsynr=0x1, cb=2
 *   iova: Erişilmeye çalışılan IOVA adresi
 *   fsr:  Fault Status Register
 *   TF:   Translation Fault (eşleme yok)
 *   PF:   Permission Fault (izin yok, eşleme var)
 *   cb:   Context Bank numarası (hangi cihaz/domain)
 */

Özel Fault Handler Kaydı

/* Sürücü içinde özel fault handler */
static int my_iommu_fault(struct iommu_domain *domain,
                          struct device *dev,
                          unsigned long iova,
                          int flags, void *token)
{
    dev_err(dev, "IOMMU fault: iova=0x%lx flags=0x%x\n",
            iova, flags);

    if (flags & IOMMU_FAULT_READ)
        dev_err(dev, "  Okuma hatasi\n");
    if (flags & IOMMU_FAULT_WRITE)
        dev_err(dev, "  Yazma hatasi\n");

    /* -ENOSYS: fault'u işlemedik, default handler devam eder */
    /* 0: fault işlendi */
    return -ENOSYS;
}

/* Domain oluşturulduktan sonra handler'ı kaydet: */
iommu_set_fault_handler(domain, my_iommu_fault, dev);

/sys/kernel/debug/iommu/ Analizi

# IOMMU durumu:
cat /sys/kernel/debug/iommu/iommu_device_list

# ARM SMMU özel debug (CONFIG_ARM_SMMU_DEBUG gerektirir):
cat /sys/kernel/debug/arm-smmu/09050000.iommu/context_bank

# IOVA tahsis istatistikleri:
cat /sys/kernel/debug/iova/ff000000.ethernet/iova_rcache
cat /sys/kernel/debug/iova/ff000000.ethernet/reserved_iova_list

Yaygın Hata Senaryoları

HataNedeniÇözüm
Translation Fault (TF)Cihaz, eşlenmemiş IOVA'ya erişiyordma_map_* çağrısı unutulmuş; sürücü IOVA'yı cihaza yazmadan önce eşleme yapmalı
Permission Fault (PF)Eşleme var ama izin yokiommu_map() çağrısında IOMMU_READ/WRITE/EXEC bayraklarını kontrol et
External AbortSMMU kendisi bir AXI hata aldıDonanım bağlantısı veya clock/power sorunu; DT yapılandırmasını kontrol et
IOVA tahsis hatasıIOVA adres alanı tükenmişdma_unmap_* çağrılarının yapıldığını doğrula; IOVA alanı boyutunu artır

08 Gömülü güvenlik tasarımı

SMMU'yu gömülü bir sistemde etkin biçimde kullanmak, donanım tasarımından başlayarak yazılım mimarisine uzanan bütüncül bir yaklaşım gerektirir. Bu bölüm Cortex-A55 tabanlı bir platform üzerinden somut öneriler sunar.

SMMUv3 ATS ve PasID

SMMUv3'ün sunduğu iki gelişmiş özellik, gömülü sistemlerde yeni kullanım senaryoları açar:

ATS (Address Translation Services) PCIe cihazlarının kendi adres çevirisini önbelleklemesini sağlar. Cihaz, TLP'lerde doğrudan fiziksel adres kullanır; SMMU latency'si ortadan kalkar. Yüksek bant genişlikli ağ ve depolama cihazları için önemlidir.
PasID (Process Address Space ID) Bir cihazın birden fazla işlemin adres alanını eş zamanlı kullanmasını sağlar. SVA (Shared Virtual Addressing) ile birlikte, cihaz işlemcinin sayfa tablosunu doğrudan paylaşır. Akıllı NIC ve GPU hesaplama için tasarlanmıştır.

Cortex-A55 Entegrasyon Örneği

/* Cortex-A55 (ARMv8.2-A) tabanlı SoC, SMMUv3 ile:
 * - 4 x Cortex-A55 @ 1.8 GHz
 * - SMMUv3.1 (128 context bank, 64-bit IOVA)
 * - LPDDR4 512 MB
 * - GbE, USB3, eMMC — hepsi SMMU'ya bağlı
 */

/* DT minimal yapılandırma: */
/*
    smmu: iommu@c0000000 {
        compatible = "arm,smmu-v3";
        reg = <0xc0000000 0x80000>;
        interrupts = <...>;
        #iommu-cells = <1>;
        arm,sid-bits = <16>;
        dma-coherent;
    };

    ethernet: eth@80000000 {
        iommus = <&smmu 0x100>;
    };

    usb: usb3@81000000 {
        iommus = <&smmu 0x200>;
    };
*/

/* Kernel config (güvenli gömülü imaj için): */
/*
    CONFIG_IOMMU_API=y
    CONFIG_ARM_SMMU_V3=y
    CONFIG_IOMMU_DEFAULT_DMA_STRICT=y
    CONFIG_IOMMU_DEFAULT_PASSTHROUGH=n
*/

Güvenli Sistem Mimarisi Önerileri

1. disable_bypass etkinleştirin SMMU kaydı olmayan cihazların bypass etmesini önleyin. Kernel parametresi: arm-smmu.disable_bypass=1. Bu sayede yanlış DT yapılandırması sessizce geçemez.
2. Her cihaza ayrı IOMMU grubu atayın Birden fazla cihazı aynı gruba koymak izolasyonu zayıflatır. Donanım tasarımında her DMA master'a benzersiz StreamID atanmasını sağlayın.
3. Minimum erişim ilkesi uygulayın Her cihaz yalnızca gerçekten erişmesi gereken bellek bölgelerine eşlenmelidir. Büyük IOVA aralıkları açmak güvenlik açığı yaratır.
4. Fault handler'ı üretim koduna ekleyin İzlenmeyen context fault'lar sessiz bozulmaya yol açabilir. Özel handler ile hataları loglar, isteğe bağlı olarak sistem durumunu güvenli moda alın.
5. SMMU sysfs izlemesini düzenli yapın /sys/kernel/debug/iommu/ üzerinden çalışma zamanı istatistiklerini izleyin. Anormal fault sayısı saldırı veya sürücü hatasının erken göstergesi olabilir.

Kısıtlı Bellek Bölgesi Tanımı

/* Belirli bir fiziksel bölgeye erişimi yalnızca güvenilir cihazla sınırla */

/* 1. Güvenli bölgeyi reserved-memory olarak DT'de tanımla: */
/*
    reserved-memory {
        #address-cells = <1>;
        #size-cells = <1>;
        secure_region: secure@20000000 {
            reg = <0x20000000 0x01000000>;  /* 16 MB */
            no-map;
        };
    };
*/

/* 2. Yalnızca crypto engine'i bu bölgeye eşle: */
static int setup_secure_dma(struct device *crypto_dev)
{
    struct iommu_domain *domain;
    phys_addr_t secure_phys = 0x20000000;
    size_t secure_size = SZ_16M;
    dma_addr_t secure_iova = 0x20000000;  /* 1:1 eşleme */

    domain = iommu_get_domain_for_dev(crypto_dev);
    if (!domain)
        return -ENODEV;

    return iommu_map(domain, secure_iova, secure_phys,
                     secure_size,
                     IOMMU_READ | IOMMU_WRITE | IOMMU_NOEXEC,
                     GFP_KERNEL);
}

TrustZone ile SMMU Tümleşimi

ARM TrustZone etkin sistemlerde SMMU, Secure ve Non-Secure dünyaları için ayrı ayrı yapılandırılabilir. Secure monitor (EL3) SMMU'nun güvenlik bölge eşlemesini kontrol eder; Normal Dünya (EL1/EL0) yalnızca kendi bölgesi için yapılandırma yapabilir. Bu yaklaşım, bootloader'dan başlayarak güvenlik mimarisinin temelini oluşturur ve gömülü güvenlik sertifikasyonlarında (IEC 62443, Common Criteria) dikkate alınan bir bileşendir.