00 i.MX8QM donanım mimarisi ve kaynak bölümlendirmesi
NXP i.MX8 QuadMax, birden fazla işlemci çekirdeği, GPU, DSP ve bağımsız güç alanlarıyla karmaşık mixed-criticality tasarımlar için uygundur. Jailhouse bu SoC için resmi destek sağlar.
i.MX8 QuadMax İşlemci Kaynakları:
├── CA72 Cluster (2 core, ARMv8-A)
│ Saat: 1.6 GHz, L2=1MB/core, L3=4MB paylaşımlı
├── CA53 Cluster (4 core, ARMv8-A)
│ Saat: 1.2 GHz, L2=512KB/core
├── CM4F (2 core, ARMv7E-M)
│ Saat: 264 MHz, TCM: 256KB ITCM + 256KB DTCM
├── HiFi4 DSP
├── GC7000L GPU
└── VPU (video encode/decode)
Bellek haritası (DDR4-3200, 6 GB):
0x80000000 – 0xBFFFFFFF Linux root (1 GB)
0xC0000000 – 0xCFFFFFFF FreeRTOS inmate (256 MB)
0xD0000000 – 0xDFFFFFFF Linux inmate (256 MB)
0xE0000000 – 0xE00FFFFF IVSHMEM (1 MB)
0x7FC00000 – 0x7FFFFFFF Jailhouse hypervisor (4 MB)
i.MX8 kartları arasında MCIMX8QM-CPU (MEK — Multi-sensory Evaluation Kit) veya Toradex Apalis i.MX8QM en yaygın geliştirme platformlarıdır. Bu rehber MEK tabanlıdır.
01 Jailhouse derleme ve kurulum
Jailhouse, Linux kaynak ağacıyla birlikte derlenir. Out-of-tree modül olarak derlenmesi önerilir; kernel versiyonuna duyarlıdır.
Çapraz derleme ortamı
sudo apt install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu \
device-tree-compiler python3-mako
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
# Jailhouse kaynak kodu
git clone https://github.com/siemens/jailhouse.git
cd jailhouse
git checkout v0.12 # kararlı sürüm
# Kernel source dizini (NXP BSP kernel)
KDIR=/path/to/linux-imx
Jailhouse derleme
# Jailhouse modülü ve araçları derle
make KDIR=$KDIR ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
BOARDDIR=configs/arm64
# Derleme çıktıları:
# driver/jailhouse.ko — kernel modülü
# hypervisor/jailhouse.bin — hypervisor binary
# tools/jailhouse — yönetim aracı
# configs/arm64/imx8qm*.cell — örnek cell config'leri
# Karta kopyala
scp driver/jailhouse.ko root@imx8:/lib/modules/$(uname -r)/extra/
scp hypervisor/jailhouse.bin root@imx8:/lib/firmware/
scp tools/jailhouse root@imx8:/usr/local/bin/
scp configs/arm64/imx8qm-mek.cell root@imx8:/etc/jailhouse/
Kernel yapılandırma gereksinimleri
CONFIG_ARM_SMMU=y
CONFIG_ARM_SMMU_V3=y
CONFIG_IOMMU_API=y
CONFIG_IOMMU_DEFAULT_PASSTHROUGH=y
CONFIG_VIRTUALIZATION=y
CONFIG_ARM_VIRT_EXT=y
CONFIG_UIO=m
CONFIG_UIO_IVSHMEM=m # IVSHMEM UIO driver
CONFIG_PCI=y # Jailhouse PCI emülasyonu için
CONFIG_KALLSYMS_ALL=y
CONFIG_MODULES=y
02 Root cell Linux yapılandırması
Root cell, Jailhouse'un başladığında aldığı mevcut Linux sistemidir. Root cell yapılandırması sistemdeki tüm donanımı listeler; inmate'lere devredilen kaynaklar root cell'den çıkarılır.
#include <jailhouse/types.h>
#include <jailhouse/cell-config.h>
#define JAILHOUSE_ARRAY_TERMINATOR { 0 }
struct {
struct jailhouse_system header;
__u64 cpus[1];
struct jailhouse_memory mem_regions[16];
struct jailhouse_irqchip irqchips[3];
} __attribute__((packed)) config = {
.header = {
.signature = JAILHOUSE_SYSTEM_SIGNATURE,
.revision = JAILHOUSE_CONFIG_REVISION,
.architecture = JAILHOUSE_ARM64,
.hypervisor_memory = {
.phys_start = 0x7fc00000,
.size = 0x00400000,
},
.debug_uart = {
.phys_start = 0x5a060000, /* LPUART0 */
.size = 0x1000,
.type = JAILHOUSE_CON_TYPE_IMX_LPUART,
.flags = JAILHOUSE_CON_ACCESS_MMIO | JAILHOUSE_CON_REGDIST_4,
},
.platform_info = {
.arm = {
.gicd_base = 0x51a00000,
.gicc_base = 0x51b00000,
.gich_base = 0x51b40000,
.gicv_base = 0x51c40000,
.gicr_base = 0x51a40000,
.maintenance_irq = 25,
.gic_version = 3,
.iommu_units = {
{
.type = JAILHOUSE_IOMMU_SMMU_V3,
.base = 0x51400000,
.size = 0x00020000,
},
},
},
},
.root_cell = {
.name = "i.MX8QM Linux",
.num_cpus = 2,
.cpu_set_size = sizeof(__u64),
.num_memory_regions = 16,
.num_irqchips = 3,
},
},
.cpus = { 0x3 }, /* CA72 core 0,1 */
/* Bellek bölgeleri — DDR, MMIO peripheral'lar */
.mem_regions = {
/* Main DDR — root Linux */
JAILHOUSE_MEM_REGION(0x80000000, 0x80000000, 0x40000000,
JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
JAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_DMA),
/* UART0 */
JAILHOUSE_MEM_REGION(0x5a060000, 0x5a060000, 0x1000,
JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE | JAILHOUSE_MEM_IO),
/* ... */
},
};
Bootloader (U-Boot) ayarları
# U-Boot konsolunda:
setenv jailhouse_args "mem=1536M@0x80000000"
setenv bootargs "console=ttyLP0,115200 root=/dev/mmcblk0p2 \
rootwait rw earlycon \
mem=1536M@0x80000000 \
isolcpus=2,3,4,5 nohz_full=2,3,4,5"
saveenv
03 FreeRTOS inmate cell yapılandırması
Inmate cell config dosyası, FreeRTOS guest'inin hangi CPU'ları, bellek bölgelerini ve peripheral'ları kullanacağını tanımlar.
#include <jailhouse/types.h>
#include <jailhouse/cell-config.h>
struct {
struct jailhouse_cell_desc desc;
__u64 cpus[1];
struct jailhouse_memory mem_regions[6];
struct jailhouse_irqchip irqchips[1];
struct jailhouse_pci_device pci_devices[1];
} __attribute__((packed)) config = {
.desc = {
.signature = JAILHOUSE_CELL_DESC_SIGNATURE,
.revision = JAILHOUSE_CONFIG_REVISION,
.name = "freertos-adas",
.flags = JAILHOUSE_CELL_PASSIVE_COMMREG,
.num_cpus = 2,
.cpu_set_size= sizeof(__u64),
.num_memory_regions = 6,
.num_irqchips = 1,
.num_pci_devices = 1,
.vpci_irq_base = 108,
.console = {
.phys_start = 0x5a070000, /* LPUART1 — inmate debug */
.size = 0x1000,
.type = JAILHOUSE_CON_TYPE_IMX_LPUART,
.flags = JAILHOUSE_CON_ACCESS_MMIO,
},
},
.cpus = { 0xC }, /* CA72 core 2,3 (bit 2,3) */
.mem_regions = {
/* FreeRTOS kod ve veri RAM'i */
{
.phys_start = 0xC0000000,
.virt_start = 0x80000000, /* Inmate için sanal adres */
.size = 0x10000000, /* 256 MB */
.flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
JAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_LOADABLE,
},
/* IVSHMEM — paylaşımlı bellek */
{
.phys_start = 0xE0000000,
.virt_start = 0xE0000000,
.size = 0x00100000, /* 1 MB */
.flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE,
},
/* LPUART1 */
{
.phys_start = 0x5a070000,
.virt_start = 0x5a070000,
.size = 0x1000,
.flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
JAILHOUSE_MEM_IO,
},
},
};
04 FreeRTOS inmate binary derleme
Jailhouse için FreeRTOS inmate, standart FreeRTOS'a Jailhouse inmates framework'ü eklenerek oluşturulur. Jailhouse kendi küçük bootloader'ını sağlar.
FreeRTOS inmate proje yapısı
freertos-inmate/
├── Makefile
├── main.c ← uygulama kodu
├── inmates/
│ ├── lib/
│ │ ├── arm64/
│ │ │ ├── gic.c ← GIC başlatma (Jailhouse'dan)
│ │ │ ├── uart.c
│ │ │ └── entry.S ← Jailhouse giriş noktası
│ │ └── string.c
├── FreeRTOS/ ← FreeRTOS kaynak
│ ├── include/
│ ├── portable/GCC/ARM_CA9/
│ └── *.c
└── ldscript.ld ← bellek düzeni
main.c — ADAS sensör görevi
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "ivshmem_protocol.h"
/* IVSHMEM base adresi (linker script'ten) */
extern ivshmem_layout_t ivshmem_region;
static ivshmem_layout_t *shm = &ivshmem_region;
static QueueHandle_t sensor_queue;
typedef struct {
float x, y, z; /* ivmeölçer verisi */
uint32_t timestamp;
} sensor_data_t;
void sensor_read_task(void *pvParameters)
{
sensor_data_t data;
while (1) {
/* Donanım sensörünü oku (I2C/SPI) */
data.x = read_accel_x();
data.y = read_accel_y();
data.z = read_accel_z();
data.timestamp = xTaskGetTickCount();
xQueueSend(sensor_queue, &data, 0);
vTaskDelay(pdMS_TO_TICKS(10)); /* 100 Hz */
}
}
void ivshmem_publish_task(void *pvParameters)
{
sensor_data_t data;
ivshmem_msg_t msg;
while (1) {
if (xQueueReceive(sensor_queue, &data, pdMS_TO_TICKS(100))) {
msg.type = 1;
msg.timestamp = data.timestamp;
memcpy(msg.payload, &data, sizeof(data));
msg.payload_len = sizeof(data);
ivshmem_ring_push(&shm->adas_to_root, &msg);
}
/* Heartbeat güncelle */
atomic_fetch_add(&shm->heartbeat_adas, 1);
}
}
void main_freertos(void)
{
sensor_queue = xQueueCreate(64, sizeof(sensor_data_t));
xTaskCreate(sensor_read_task, "SensorRead", 512, NULL, 3, NULL);
xTaskCreate(ivshmem_publish_task,"IVSHPub", 512, NULL, 2, NULL);
vTaskStartScheduler();
}
Derleme ve yükleme
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \
JAILHOUSE_DIR=/path/to/jailhouse
# freertos-adas.bin üretildi
# Karta kopyala ve inmate'i başlat
scp freertos-adas.bin root@imx8:/tmp/
# Jailhouse üzerinden:
jailhouse cell create /etc/jailhouse/imx8qm-freertos-inmate.cell
jailhouse cell load freertos-adas /tmp/freertos-adas.bin
jailhouse cell start freertos-adas
# Konsol çıktısını izle (LPUART1)
minicom -D /dev/ttyLP1
05 Linux inmate (ikinci Linux guest)
Jailhouse, tam bir Linux sistemi de inmate olarak çalıştırabilir. Bu, Android IVI veya güvenli Linux partition'ları için kullanılır.
Linux inmate cell config
.desc = {
.name = "linux-ivi",
.num_cpus = 4,
.flags = JAILHOUSE_CELL_VIRTUAL_CONSOLE_PERMITTED,
},
.cpus = { 0x3C }, /* CA53 core 2,3,4,5 (bit 2-5) */
.mem_regions = {
{
.phys_start = 0xD0000000,
.virt_start = 0x80000000,
.size = 0x10000000, /* 256 MB */
.flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
JAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_LOADABLE |
JAILHOUSE_MEM_DMA,
},
/* GPU (GC7000) — sadece IVI cell'e tahsis edilmiş */
{
.phys_start = 0x38000000,
.virt_start = 0x38000000,
.size = 0x80000,
.flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
JAILHOUSE_MEM_IO,
},
/* IVSHMEM */
{ .phys_start = 0xE0000000, .virt_start = 0xE0000000,
.size = 0x100000, .flags = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE },
},
Linux inmate başlatma
jailhouse cell create /etc/jailhouse/imx8qm-linux-inmate.cell
# Linux guest için: Image (kernel), DTB, rootfs
jailhouse cell load linux-ivi \
/boot/Image --address 0x80200000 \
/boot/inmate-ivi.dtb --address 0x87e00000 \
--initrd /opt/ivi-rootfs.cpio.gz --address 0x88000000
# Kernel komut satırı
jailhouse cell load linux-ivi \
--string "console=ttyLP2,115200 root=/dev/ram0 \
mem=240M@0x80000000 earlycon" \
--address 0x87f00000
jailhouse cell start linux-ivi
# IVI Linux konsolunu izle
minicom -D /dev/ttyLP2
06 IVSHMEM paylaşımlı bellek kurulumu
IVSHMEM, Jailhouse'un cell'ler arası paylaşımlı bellek mekanizmasıdır. Root cell ve tüm inmate'ler aynı fiziksel bellek bölgesini farklı sanal adreslerle görür.
Linux'ta IVSHMEM sürücüsü
# UIO sürücüsünü yükle
modprobe uio
modprobe uio_ivshmem
# IVSHMEM cihazı
ls /dev/uio*
# /dev/uio0 ← IVSHMEM
# Cihaz bilgisi
cat /sys/class/uio/uio0/name
# ivshmem
cat /sys/class/uio/uio0/maps/map0/size
# 0x100000 → 1 MB
# Root cell'den IVSHMEM'e eriş
cat > /tmp/test_ivshmem.py <<'EOF'
import mmap, struct, time
with open("/dev/uio0", "r+b") as f:
shm = mmap.mmap(f.fileno(), 0x100000)
# ADAS cell heartbeat'ini oku (offset 0x800)
shm.seek(0x800)
last = 0
for _ in range(10):
val = struct.unpack("I", shm.read(4))[0]
print(f"FreeRTOS heartbeat: {val} (delta={val-last})")
last = val
shm.seek(0x800)
time.sleep(0.5)
EOF
python3 /tmp/test_ivshmem.py
07 Cell'ler arası iletişim protokolü tasarımı
IVSHMEM üzerinde verimli, lock-free iletişim için ring buffer protokolü tasarlanır. Producer-consumer modeli, her iki tarafın bağımsız çalışmasını sağlar.
#pragma once
#include <stdint.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <string.h>
#define RING_CAPACITY 64 /* 2'nin kuvveti olmalı */
#define RING_MASK (RING_CAPACITY - 1)
typedef struct {
uint32_t type;
uint32_t seq;
uint32_t ts_ms;
uint32_t len;
uint8_t data[112]; /* toplam 128 byte */
} __attribute__((packed)) ipc_msg_t;
typedef struct {
_Atomic uint32_t head; /* üretici yazar */
_Atomic uint32_t tail; /* tüketici yazar */
uint32_t _pad[14];
ipc_msg_t slots[RING_CAPACITY];
} ipc_ring_t;
static inline bool ipc_ring_push(ipc_ring_t *r, const ipc_msg_t *m)
{
uint32_t head = atomic_load_explicit(&r->head, memory_order_relaxed);
uint32_t next = (head + 1) & RING_MASK;
if (next == atomic_load_explicit(&r->tail, memory_order_acquire))
return false; /* dolu */
r->slots[head] = *m;
atomic_store_explicit(&r->head, next, memory_order_release);
return true;
}
static inline bool ipc_ring_pop(ipc_ring_t *r, ipc_msg_t *m)
{
uint32_t tail = atomic_load_explicit(&r->tail, memory_order_relaxed);
if (tail == atomic_load_explicit(&r->head, memory_order_acquire))
return false; /* boş */
*m = r->slots[tail];
atomic_store_explicit(&r->tail, (tail + 1) & RING_MASK,
memory_order_release);
return true;
}
Mesaj tipleri ve protokol
Tip Ad Kaynak Hedef Periyot
────────────────────────────────────────────────────────────
0x01 SENSOR_IMU FreeRTOS Linux root 10ms
0x02 SENSOR_CAN FreeRTOS Linux root 5ms
0x03 COMMAND Linux root FreeRTOS talep üzerine
0x04 HEARTBEAT Her ikisi Her ikisi 100ms
0x05 LOG FreeRTOS Linux root olay üzerine
0x06 STATUS FreeRTOS Linux root 1000ms
08 Hata ayıklama ve izleme
Jailhouse sistemlerde hata ayıklama, tek çekirdekli sistemden daha karmaşıktır. Her cell bağımsız konsol kullanır; hypervisor UART multiplexing sağlar.
Jailhouse sanal konsol
# Tüm hücrelerin konsol çıktısını birleştir
jailhouse console
# Belirli cell'i izle (pseudo-terminal)
jailhouse cell console freertos-adas
# Cell istatistikleri
jailhouse cell stats freertos-adas
# CPU kullanımı, interrupt sayıları, bellek ihlalleri
GDB ile FreeRTOS inmate debug
# OpenOCD ile i.MX8QM JTAG bağlantısı
openocd -f interface/jlink.cfg \
-f target/imx8qm.cfg \
-c "init; targets"
# GDB ile FreeRTOS inmate'e bağlan (CA72 core 2)
aarch64-linux-gnu-gdb freertos-adas.elf
(gdb) target extended-remote localhost:3333
(gdb) monitor targets imx8qm.cpu2
(gdb) break main_freertos
(gdb) continue
IVSHMEM mesaj istatistikleri
import mmap, struct, time
with open("/dev/uio0", "r+b") as f:
shm = mmap.mmap(f.fileno(), 0x100000)
ring_offset = 0 # adas_to_root ring başlangıcı
while True:
shm.seek(ring_offset)
head = struct.unpack("I", shm.read(4))[0]
tail = struct.unpack("I", shm.read(4))[0]
used = (head - tail) % 64
print(f"Ring kullanımı: {used}/64 head={head} tail={tail}")
time.sleep(1)
09 Üretim için sertleştirme adımları
Geliştirme ortamından üretim sistemine geçişte güvenlik ve kararlılık için ek adımlar gerekmektedir.
Secure boot zinciri
ROM Bootloader (donanımda)
↓ (RSA-4096 imza doğrulama)
U-Boot (HABv4 imzalı)
↓ (FIT Image doğrulama)
Linux Kernel (imzalı)
↓
Jailhouse (Linux modülü olarak yükle)
↓
Cell yapılandırmaları (dm-verity korumalı /etc/jailhouse)
↓
FreeRTOS + Linux inmate binary'leri (imzalı)
Üretim kontrol listesi
# Tüm cell'lerin çalışır durumda olduğunu doğrula
check_cells() {
local expected=("i.MX8QM Linux" "freertos-adas" "linux-ivi")
for name in "${expected[@]}"; do
state=$(jailhouse cell list | grep "$name" | awk '{print $3}')
if [ "$state" != "running" ]; then
logger -t jailhouse "HATA: $name cell durumu: $state"
# Yeniden başlatma kararı (safety monitor'a ilet)
fi
done
}
# systemd timer veya cron ile her 30 saniyede çalıştır
check_cells