Diagram showing a Genio 720 SoC partitioned into a Linux root cell and Zephyr inmate cell by the Jailhouse hypervisor at EL2
mediatekgeniojailhousehypervisorzephyrreal-timeembedded linux

Jailhouse hypervisor on MediaTek Genio: bare-metal isolation on Linux

Andres Campos ·

Jailhouse is a partitioning hypervisor that splits a SoC into isolated cells rather than virtualizing hardware. On MediaTek Genio 720, it runs at EL2 and gives dedicated CPU cores, memory, and peripherals exclusively to a Zephyr inmate cell while Linux continues on the remaining cores. Getting it running on Genio requires three kernel patches that aren’t in any standard guide, plus MediaTek-specific cell config fields that upstream Jailhouse documentation doesn’t cover.

Key Insights

  • Jailhouse runs at EL2; Linux and Zephyr both run at EL1. On Genio 720, the hypervisor sits at 0x44000000 (16MB) with the GIC at 0x0c000000.
  • Three kernel patches are required: disable VHE in finalise_el2, export ioremap_page_range and __get_vm_area_caller from vmalloc.c, and add a 16MB reserved-memory DTS node at 0x44000000.
  • Standard ARM Jailhouse peripheral passthrough (MMIO region + disabled DTS node) is not enough on Genio — you also need a .vendors array with JAILHOUSE_VENDOR_MTK_GPIO and JAILHOUSE_VENDOR_MTK_EINT entries.
  • The hypervisor memory region must not overlap the Linux kernel image — upgrading from Linux 5.x to 6.6 on Genio 700/720 required shifting the base address by ~48MB as the kernel image grew.
  • Current status (2026): root cell and Zephyr basic boot with UART working on Genio 720 EVK. GPIO passthrough functional via the MTK vendor config. SPI and I2C inmate bring-up in progress.

What Jailhouse does and why it matters on Genio

Jailhouse doesn’t virtualize hardware. It partitions it. The SoC is divided into cells at startup, and each cell gets exclusive ownership of specific CPU cores, memory regions, and peripherals. There’s no scheduling between cells — each runs uninterrupted on its assigned cores.

The typical setup on Genio 720:

  • Root cell (Linux): 6 of 8 CPU cores, most RAM, full peripheral access
  • Inmate cell (Zephyr): 1-2 dedicated cores, 48MB isolated memory, UART + GPIO + EINT passthrough
  • Jailhouse itself: runs at EL2, 16MB at 0x44000000

Linux and Zephyr both run at EL1. Jailhouse at EL2 enforces the partition boundaries. From the inmate’s perspective, Linux doesn’t exist.

Communication between cells uses IVSHMEM, which appears as a PCI device in both cells. On Genio 720, the IVC communication region is 4KB at physical 0x80000000.

Current status (2026): root cell (Linux) working on Genio 720 EVK. Zephyr inmate has basic boot and UART working. GPIO is supported via the MediaTek vendor config. SPI and I2C inmate bring-up are in progress.

Three required kernel patches

All three patches are required. Without any one of them, Jailhouse fails to initialize.

Patch 1 — Disable VHE (arch/arm64/kernel/hyp-stub.S):

Add an early return in finalise_el2 to prevent Linux from taking EL2:

ENTRY(finalise_el2)
    ret    /* early return — do not enable VHE */
    ...

Export the EL2 stub vector table:

EXPORT_SYMBOL_GPL(__hyp_stub_vectors)

Patch 2 — Export kernel symbols (mm/vmalloc.c):

// Remove pgprot_nx from ioremap
EXPORT_SYMBOL_GPL(ioremap_page_range)
EXPORT_SYMBOL_GPL(__get_vm_area_caller)

These are used by jailhouse.ko at runtime to set up hypervisor memory space.

Patch 3 — Reserve hypervisor memory in DTS:

reserved-memory {
    jailhouse@44000000 {
        reg = <0x0 0x44000000 0x0 0x01000000>; /* 16 MB */
        no-map;
    };
};

The Genio 720 cell config — real addresses

The cell config is a C struct compiled into a .cell file. Real values from the Genio 720:

.platform_info = {
    .gic_version    = 3,
    .gicd_base      = 0x0c000000,
    .gicr_base      = 0x0c040000,  /* 2MB — covers 8 CPU redistributors */
    .maintenance_irq = 25,
},

Inmate memory region (48MB):

{
    .phys_start = 0x45000000,
    .virt_start = CONFIG_INMATE_BASE,
    .size       = 0x03000000,   /* 48 MB */
    .flags      = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
                  JAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_LOADABLE,
},

UART1 shared with root cell (inmate console):

{
    .phys_start = 0x11002000,
    .size       = 0x1000,
    .flags      = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |
                  JAILHOUSE_MEM_IO | JAILHOUSE_MEM_ROOTSHARED | JAILHOUSE_MEM_IO_32,
},

IVC communication region:

{
    .virt_start = 0x80000000,
    .size       = 0x00001000,
    .flags      = JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE | JAILHOUSE_MEM_COMM_REGION,
},

Inmate IRQs: SPI 446 (GPIO interrupt), SPI 267 (EINT interrupt).

The MediaTek vendor config — what makes Genio different

On standard ARM platforms (i.MX, Jetson, QEMU), passing a peripheral to a Jailhouse inmate requires only an MMIO region with JAILHOUSE_MEM_IO and disabling the peripheral in the Linux DTS.

On MediaTek Genio, that’s not enough. You also need a .vendors array to configure GPIO pin mux and EINT interrupt routing. Without it, the inmate can address the peripheral’s MMIO but can’t use GPIO pins or receive interrupts.

For UART1 passthrough on Genio 720:

.vendors = {
    /* GPIO pin mux for UART1 TX/RX (pins 33 & 34) */
    {
        .type = JAILHOUSE_VENDOR_MTK_GPIO,
        .mtk_gpio.address  = 0x10005000,
        .mtk_gpio.pin_base = 32,
        .mtk_gpio.pin_bitmap = {
            0x00000006, 0, 0, 0
        }
    },
    /* EINT routing for UART interrupt */
    {
        .type = JAILHOUSE_VENDOR_MTK_EINT,
        .mtk_eint.address  = 0x1000b000,
        .mtk_eint.pin_base = 32,
        .mtk_eint.pin_bitmap = {
            0x00000140, 0, 0, 0
        }
    },
},

GPIO base: 0x10005000. EINT base: 0x1000b000. Every peripheral that uses GPIO or interrupts needs corresponding entries. This is not in the standard Jailhouse documentation — it’s MediaTek-specific.

The hypervisor memory sizing gotcha

The hypervisor memory region must not overlap with the Linux kernel image. The kernel binary grows with each version — and this causes real problems in practice.

Upgrading from Linux 5.x to 6.6 on Genio 700/720 required shifting the hypervisor base address by approximately 48MB. The kernel image had grown enough to overlap the previously-safe hypervisor region.

To find the actual kernel footprint on a running system:

cat /proc/iomem | grep Kernel

Leave at least 16MB of headroom above the expected kernel load address. The kernel grows faster than expected across major version bumps.

When Jailhouse makes sense — and when it doesn’t

Jailhouse is the right answer when you need hard real-time behavior on the same SoC as a Linux application — motor control, safety monitoring, deterministic sensor sampling alongside AI inference. The isolation is genuine: the inmate cell’s timing is not affected by Linux scheduler behavior or interrupt storms.

It’s not the right answer if you just need faster interrupt response from Linux. For that, look at CONFIG_PREEMPT_RT or processor affinity.

The Genio 720’s 8-core layout (2×A78 + 6×A55) fits this pattern well: run Zephyr on one A55 core with dedicated peripherals, Linux on the remaining 7 cores. The A55 cores have sufficient headroom for real-time tasks while the A78 cores handle AI inference.

For Genio Jailhouse bring-up — kernel patch through IVSHMEM communication — we handle this as part of custom embedded engagements at ProventusNova.

Frequently Asked Questions

What is Jailhouse and why use it on MediaTek Genio?

Jailhouse partitions the Genio SoC into isolated cells — Linux on most cores, Zephyr on dedicated cores for hard real-time tasks. Each cell has exclusive ownership of its CPU cores, memory, and peripherals.

What exception level does Jailhouse run at on Genio?

Jailhouse runs at EL2. Linux and Zephyr run at EL1. A required kernel patch prevents Linux from claiming EL2 at startup.

How do Linux and Zephyr communicate in a Jailhouse setup?

Via IVSHMEM, mapped as a virtual PCI device in both cells. Jailhouse provides an IVC API on top. On Genio 720, the IVC region is 4KB at physical 0x80000000.

Why do I need the MTK vendor config for peripheral passthrough?

Standard ARM Jailhouse only needs an MMIO region and disabled DTS node. MediaTek Genio requires a .vendors array with JAILHOUSE_VENDOR_MTK_GPIO and JAILHOUSE_VENDOR_MTK_EINT entries to configure pin mux and interrupt routing.

What kernel patches are required for Jailhouse on Genio?

Three patches: (1) disable VHE in finalise_el2 and export __hyp_stub_vectors, (2) export ioremap_page_range and __get_vm_area_caller from vmalloc.c, (3) add a 16MB reserved-memory DTS node at 0x44000000.

MediaTek Genio Expert Support

Building on MediaTek Genio?

BSP bring-up, GStreamer pipelines, NeuroPilot integration — we've shipped it. Get unblocked fast. One call to scope it, fixed bid to deliver it.

Frequently Asked Questions

What is Jailhouse and why use it on MediaTek Genio?

Jailhouse is an ARM partitioning hypervisor that splits an SoC into isolated cells — a Linux root cell handling most peripherals and a bare-metal or RTOS inmate cell running hard real-time code. On Genio 720, this means running Zephyr on dedicated CPU cores with deterministic timing while Linux handles connectivity, AI inference, and UI on the remaining cores.

What exception level does Jailhouse run at on Genio?

Jailhouse runs at EL2. Linux and Zephyr both run at EL1. One required kernel patch disables VHE by adding an early return to finalise_el2 in arch/arm64/kernel/hyp-stub.S — without this, Linux claims EL2 for itself and Jailhouse cannot initialize.

How do Linux and Zephyr communicate in a Jailhouse setup?

Via IVSHMEM (Inter-VM Shared Memory), mapped as a virtual PCI device in both cells. Jailhouse provides an IVC API on top for message passing. On Genio 720, the IVC region is 4KB at physical 0x80000000.

Why do I need the MTK vendor config for peripheral passthrough on Genio?

MediaTek Genio uses a proprietary GPIO and EINT architecture that standard Jailhouse doesn't handle. On standard ARM, passing a peripheral requires only an MMIO region and disabling the device in Linux DTS. On Genio, you also need a .vendors array with JAILHOUSE_VENDOR_MTK_GPIO and JAILHOUSE_VENDOR_MTK_EINT entries to configure pin mux and interrupt routing.

What kernel patches are required for Jailhouse on ARM64 Genio?

Three patches: (1) Disable VHE in arch/arm64/kernel/hyp-stub.S and export __hyp_stub_vectors. (2) In mm/vmalloc.c: remove pgprot_nx from ioremap and export ioremap_page_range and __get_vm_area_caller. (3) Add a reserved-memory node in the platform DTS for the 16MB Jailhouse hypervisor region at 0x44000000.

Andrés Campos, Co-Founder & CTO at ProventusNova

Written by

Andrés Campos

Co-Founder & CTO · ProventusNova

8 years deep in embedded systems — from underwater ROVs to edge AI. Andrés leads every technical delivery personally.

Connect on LinkedIn