From: Sebastian Ene <sebastianene@google.com>
To: catalin.marinas@arm.com, fuad.tabba@linux.dev,
joey.gouly@arm.com, mark.rutland@arm.com, maz@kernel.org,
oupton@kernel.org, rananta@google.com, Sascha.Bischoff@arm.com,
suzuki.poulose@arm.com, will@kernel.org
Cc: kvmarm@lists.linux.dev, android-kvm@google.com,
bgrzesik@google.com, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, nathan@kernel.org,
perlarsen@google.com, sebastianene@google.com,
seiden@linux.ibm.com, smostafa@google.com, tglx@kernel.org,
vdonnefort@google.com, vladimir.murzin@arm.com,
yuzenghui@huawei.com, zenghui.yu@linux.dev
Subject: [PATCH v2 00/13] KVM: ITS hardening for pKVM
Date: Fri, 7 Aug 2026 16:43:10 +0000 [thread overview]
Message-ID: <20260807164322.2970811-2-sebastianene@google.com> (raw)
This series introduces the necessary machinery to perform trap & emulate
on device access in pKVM. Furthermore, it hardens the GIC/ITS controller to
prevent an attacker from tampering with the hypervisor protected memory
through this device.
In pKVM, the host kernel is initially trusted to manage the boot process but
its permissions are revoked once KVM initializes. The GIC/ITS device is
configured before the kernel deprivileges itself. Once the hypervisor
becomes available, sanitize the accesses to the ITS controller by
trapping and emulating certain registers and by shadowing some memory
structures used by the ITS.
This is required because the ITS is in complete host control and can issue
transactions on the memory bus without having any protection in front of it
(IOMMU or SMMU). With pKVM this makes it an interesting target for crossing the
hypervisor privilege boundary because with pKVM the security model is
that the host is untrusted.
Patches overview
================
The first patch is an adaptation of Mostafa's patch for donating MMIO to
the hypervisor which can be found here [1]
[1] https://lore.kernel.org/all/20260715115906.2664882-3-smostafa@google.com/
The only difference is that this maps MMIO in the linear space of the
hypervisor, but after discussing with him on the list [2]
(https://lore.kernel.org/all/anMwxy9gEEt_Wu6M@google.com/) he seems to be
more inclined to having the IO in the private VA mapping space. For
simplicity, I used the linear map but I am happy to change it this is
required as it doesn't change the code too much.
The next 2 patches add some basic infrastructure to be able to keep
track of the unmapped MMIO regions from the host and invoke a handler
when a stage-2 data abort happens in that region.
The 4th patch looks up the ITS node from the device tree and adds it to
an array of unmapped devices. It install a handler that forwards all the
MMIO request to mediate the host access inside the emulation layer and
to prevent breaking ITS functionality.
The 5th patch changes the GIC/ITS driver to exposes two new methods
which will be called from the KVM layer to setup the shadow state and
to take the appropriate locks.
Patch 6 adds the entry point into the emulation setup and sets up the
shadow command queue. It adds some helper macros to define the offset
register and the associate action that we want to execute in the
emulation. It also unmaps the state passed from the host kernel
to prevent it from playing nasty games later on. The patch
traps accesses to CWRITER register and copies the commands from the
host command queue to the shadow command queue.
Patch 7 prevents the host from directly accessing the first layer of the
indirect tables held in GITS_BASER<n>. It also prevents the host from
directly accesssing the last layer of the Device Table (since the entries
in this table hold the address of the ITT table) and of the vPE Table
(since the vPE table entries hold the address of the virtual LPI pending
table.
Patches [8-10] sanitize the commands sent to the ITS and their
arguments.
The next patches restrict the access of the host to certain registers
and prevent undefined behaviour. Prevent the host from re-programming
the tables held in the GITS_BASER register.
The last patch introduces an hvc to setup the ITS emulation and calls
into the ITS driver to setup the shadow state.
Design
======
1. Command queue shadowing
The ITS hardware supports a command queue which is programmed by the driver
in the GITS_CBASER register. When ITS emulation is enabled, the ITS
driver allocated a copy of the original command queue and uses the copy
and the emulation layer restricts the access to the original queue for
the host. When the driver sends a command to the mirrored queue, the
emulation traps on the write to GITS_CWRITER register and validates the
command before writing it over to the original queue.
2. Indirect tables first level shadowing
The ITS hardware supports indirection to minimize the space required to
accommodate large tables (eg. deviceId space used to index the Device Table
is quite sparse). This is a 2-level indirection, with entries from the
first table pointing to a second table.
An attacker in control of the host can insert an address that points to
the hypervisor protected memory in the first level table and then use
subsequent ITS commands to write to this memory (MAPD).
To shadow this tables, we rely on the driver to allocate space for it
and we copy the original content from the table into the copy. When
pKVM becomes available we switch the pointers that hold the orginal
tables to point to the copy.
To keep the tables from the hypervisor in sync with what the host
has, we update the tables when commands are sent to the ITS.
3. Hiding the last layer of the Device Table and vPE Table from the host
An attacker in control of the host kernel can alter the content of these
tables directly (the Arm IHI 0069H.b spec says that is undefined behavior
if entries are created by software). Normally these entries are created in
response of commands sent to the ITS.
A Device Table entry that has the following structure:
type DeviceTableEntry is (
boolean Valid,
Address ITT_base,
bits(5) ITT_size
)
This can be maliciously created by an attacker and the ITT_base can be
pointed to hypervisor protected memory. The MAPTI command can then be
used to write over the ITT_base with an ITE entry.
Similarly a vCPU Table entry has the following structure:
type VCPUTableEntry is (
boolean Valid,
bits(32) RDbase,
Address VPT_base,
bits(5) VPT_size
)
VPT_base can be pointed to hypervisor protected memory and then a
command can be used to raise interrupts and set the corresponding
bit. This would give a 1-bit write primitive so is not "as generous"
as the others.
Testing
=======
Verified that it boots in Qemu using the following arguments:
"-machine virt,virtualization=true,gic-version=4,its=true,iommu=smmuv3,acpi=off"
and enabled Qemu tracing using the following argument: '--trace "gicv3_its_*"'
Qemu reports a bunch of events due to virtio-block using the ITS to
raise MSIs and I can see the accesses made by the device to the
GITS_TRANSLATER register.
Wrote a PoC [3] which uses a DEVMEM like interface to poke the ITS
directly from userspace to achieve controlled write to hyp protected
memory and verified that this is not possible anymore when the
sanitization is in place.
[3] [https://github.com/sebastianene07/gic-its-poc/blob/main/its.c]
What is not covered
===================
Only a subset of the commands is sanitized & covered: vLPI commands are
not covered at all and the Redistributor hardeninig is not covered.
Changelog
=========
v1 -> v2:
- fixed a bunch of functional errors in the parsing of the MAPD command
- renamed the functions exported from the GIC driver, made the
allocation GFP_ATOMIC, make it accepts a flags arg
- renamed the entry point in the emulation and make it so that it
accepts the size of pages that we use for the private state
- added support for rollback on certain commands that timeout on
hardware
- clarified commit messages
Previous posting:
################
v1: https://lore.kernel.org/all/20260310124933.830025-1-sebastianene@google.com/
Mostafa Saleh (1):
KVM: arm64: Donate MMIO to the hypervisor
Sebastian Ene (12):
KVM: arm64: Track host-unmapped MMIO regions in a static array
KVM: arm64: Support host MMIO trap handlers for unmapped devices
KVM: Parse the device tree and register the ITS region with pKVM
irqchip/gic-v3-its: Add support for the ITS emulation setup
KVM: arm64: Shadow the ITS command queue and setup emulation
KVM: arm64: Restrict host access to the private ITS tables
KVM: arm64: Trap & emulate the ITS MAPD command
KVM: arm64: Trap & emulate the ITS MAPC command
KVM: arm64: Restrict host updates to GITS_CTLR
KVM: arm64: Prevent the host from specifying a different command queue
KVM: arm64: Prevent the host from programming new GITS_BASER tables
KVM: arm64: Implement HVC interface for ITS emulation setup
arch/arm64/include/asm/kvm_arm.h | 2 +
arch/arm64/include/asm/kvm_asm.h | 1 +
arch/arm64/include/asm/kvm_pkvm.h | 18 +
arch/arm64/kvm/hyp/include/nvhe/its_emulate.h | 14 +
arch/arm64/kvm/hyp/include/nvhe/mem_protect.h | 7 +
arch/arm64/kvm/hyp/nvhe/Makefile | 3 +-
arch/arm64/kvm/hyp/nvhe/hyp-main.c | 16 +
arch/arm64/kvm/hyp/nvhe/its_emulate.c | 827 ++++++++++++++++++
arch/arm64/kvm/hyp/nvhe/mem_protect.c | 189 +++-
arch/arm64/kvm/hyp/nvhe/setup.c | 27 +
arch/arm64/kvm/hyp/pgtable.c | 11 +-
arch/arm64/kvm/pkvm.c | 76 +-
drivers/irqchip/irq-gic-v3-its.c | 169 +++-
include/linux/irqchip/arm-gic-v3.h | 51 ++
14 files changed, 1379 insertions(+), 32 deletions(-)
create mode 100644 arch/arm64/kvm/hyp/include/nvhe/its_emulate.h
create mode 100644 arch/arm64/kvm/hyp/nvhe/its_emulate.c
--
2.55.0.654.g21b8a5bc05-goog
next reply other threads:[~2026-08-07 16:43 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-07 16:43 Sebastian Ene [this message]
2026-08-07 16:43 ` [PATCH v2 01/13] KVM: arm64: Donate MMIO to the hypervisor Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 02/13] KVM: arm64: Track host-unmapped MMIO regions in a static array Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 03/13] KVM: arm64: Support host MMIO trap handlers for unmapped devices Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 04/13] KVM: Parse the device tree and register the ITS region with pKVM Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 05/13] irqchip/gic-v3-its: Add support for the ITS emulation setup Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 06/13] KVM: arm64: Shadow the ITS command queue and setup emulation Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 07/13] KVM: arm64: Restrict host access to the private ITS tables Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 08/13] KVM: arm64: Trap & emulate the ITS MAPD command Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 09/13] KVM: arm64: Trap & emulate the ITS MAPC command Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 10/13] KVM: arm64: Restrict host updates to GITS_CTLR Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 11/13] KVM: arm64: Prevent the host from specifying a different command queue Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 12/13] KVM: arm64: Prevent the host from programming new GITS_BASER tables Sebastian Ene
2026-08-07 16:43 ` [PATCH v2 13/13] KVM: arm64: Implement HVC interface for ITS emulation setup Sebastian Ene
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260807164322.2970811-2-sebastianene@google.com \
--to=sebastianene@google.com \
--cc=Sascha.Bischoff@arm.com \
--cc=android-kvm@google.com \
--cc=bgrzesik@google.com \
--cc=catalin.marinas@arm.com \
--cc=fuad.tabba@linux.dev \
--cc=joey.gouly@arm.com \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=maz@kernel.org \
--cc=nathan@kernel.org \
--cc=oupton@kernel.org \
--cc=perlarsen@google.com \
--cc=rananta@google.com \
--cc=seiden@linux.ibm.com \
--cc=smostafa@google.com \
--cc=suzuki.poulose@arm.com \
--cc=tglx@kernel.org \
--cc=vdonnefort@google.com \
--cc=vladimir.murzin@arm.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
--cc=zenghui.yu@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).