* [PATCH kvmtool v1 0/5] Add guest_memfd support for arm64
@ 2026-07-06 8:35 Fuad Tabba
2026-07-06 8:35 ` [PATCH kvmtool v1 1/5] Initialize the return value in kvm__for_each_mem_bank() Fuad Tabba
` (4 more replies)
0 siblings, 5 replies; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 8:35 UTC (permalink / raw)
To: kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Suzuki K Poulose,
Andre Przywara, Oliver Upton, Marc Zyngier, Fuad Tabba
Hi folks,
Due to popular demand, i.e., Alexandru asking me about it, I repolished my
guest_memfd support series. The one I sent years ago was stale and
over-engineered, so this is a fresh start instead.
This series adds support for backing guest RAM with guest_memfd on
arm64, for non-protected VMs under both regular KVM and pKVM.
The first two patches are standalone fixes in the code this series
builds on: an uninitialized return value in kvm__for_each_mem_bank(),
and doubled newlines in die() messages. The next two switch memory
registration to kvm_userspace_memory_region2 unconditionally and add
guest_memfd plumbing to kvm__register_mem().
A new --guest-memfd option creates guest RAM from a guest_memfd with
mmap support (GUEST_MEMFD_FLAG_MMAP and GUEST_MEMFD_FLAG_INIT_SHARED,
Linux 6.18+), maps it to provide the userspace mapping, and registers
it with KVM_SET_USER_MEMORY_REGION2.
Protected VMs need in-place shared/private conversion of guest_memfd,
which is still in progress in the kernel [1], so --protected rejects
the option.
Based on kvmtool master (ca0ddafa941d).
Cheers,
/fuad
[1] https://lore.kernel.org/all/20260618-gmem-inplace-conversion-v8-0-9d2959357853@google.com/
Fuad Tabba (5):
Initialize the return value in kvm__for_each_mem_bank()
Remove newline from end of die() aborts
Use kvm_userspace_memory_region2 for all memory registration
Add guest_memfd support to kvm__register_mem()
arm64: Add --guest-memfd option to back guest RAM with guest_memfd
arm64/include/kvm/kvm-config-arch.h | 5 +++-
arm64/kvm.c | 38 +++++++++++++++++++------
hw/cfi_flash.c | 2 +-
include/kvm/kvm.h | 16 +++++++++--
include/kvm/util.h | 1 +
kvm.c | 26 +++++++++++------
util/util.c | 44 +++++++++++++++++++++++++----
7 files changed, 106 insertions(+), 26 deletions(-)
--
2.39.5
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH kvmtool v1 1/5] Initialize the return value in kvm__for_each_mem_bank()
2026-07-06 8:35 [PATCH kvmtool v1 0/5] Add guest_memfd support for arm64 Fuad Tabba
@ 2026-07-06 8:35 ` Fuad Tabba
2026-07-06 8:35 ` [PATCH kvmtool v1 2/5] Remove newline from end of die() aborts Fuad Tabba
` (3 subsequent siblings)
4 siblings, 0 replies; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 8:35 UTC (permalink / raw)
To: kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Suzuki K Poulose,
Andre Przywara, Oliver Upton, Marc Zyngier, Fuad Tabba
If none of the bank types match, the function would return an
uninitialized value.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
kvm.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/kvm.c b/kvm.c
index 7a52c6a..95ccfcb 100644
--- a/kvm.c
+++ b/kvm.c
@@ -383,14 +383,15 @@ u64 host_to_guest_flat(struct kvm *kvm, void *ptr)
* their type.
*
* If one call to @fun returns a non-zero value, stop iterating and return the
- * value. Otherwise, return zero.
+ * value. If none of the bank types match, return -ENODEV. Otherwise, return
+ * zero.
*/
int kvm__for_each_mem_bank(struct kvm *kvm, enum kvm_mem_type type,
int (*fun)(struct kvm *kvm, struct kvm_mem_bank *bank, void *data),
void *data)
{
- int ret;
struct kvm_mem_bank *bank;
+ int ret = -ENODEV;
list_for_each_entry(bank, &kvm->mem_banks, list) {
if (type != KVM_MEM_TYPE_ALL && !(bank->type & type))
--
2.39.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH kvmtool v1 2/5] Remove newline from end of die() aborts
2026-07-06 8:35 [PATCH kvmtool v1 0/5] Add guest_memfd support for arm64 Fuad Tabba
2026-07-06 8:35 ` [PATCH kvmtool v1 1/5] Initialize the return value in kvm__for_each_mem_bank() Fuad Tabba
@ 2026-07-06 8:35 ` Fuad Tabba
2026-07-06 10:53 ` Suzuki K Poulose
2026-07-06 8:35 ` [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration Fuad Tabba
` (2 subsequent siblings)
4 siblings, 1 reply; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 8:35 UTC (permalink / raw)
To: kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Suzuki K Poulose,
Andre Przywara, Oliver Upton, Marc Zyngier, Fuad Tabba
die() already adds the newline at the end of the string.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
util/util.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/util/util.c b/util/util.c
index 6f41e2c..05d49c4 100644
--- a/util/util.c
+++ b/util/util.c
@@ -128,14 +128,14 @@ void *mmap_hugetlbfs(struct kvm *kvm, const char *htlbfs_path, u64 size)
unsigned long blk_size;
if (statfs(htlbfs_path, &sfs) < 0)
- die("Can't stat %s\n", htlbfs_path);
+ die("Can't stat %s", htlbfs_path);
if ((unsigned int)sfs.f_type != HUGETLBFS_MAGIC)
- die("%s is not hugetlbfs!\n", htlbfs_path);
+ die("%s is not hugetlbfs!", htlbfs_path);
blk_size = (unsigned long)sfs.f_bsize;
if (sfs.f_bsize == 0 || blk_size > size) {
- die("Can't use hugetlbfs pagesize %ld for mem size %lld\n",
+ die("Can't use hugetlbfs pagesize %ld for mem size %lld",
blk_size, (unsigned long long)size);
}
@@ -144,10 +144,10 @@ void *mmap_hugetlbfs(struct kvm *kvm, const char *htlbfs_path, u64 size)
snprintf(mpath, PATH_MAX, "%s/kvmtoolXXXXXX", htlbfs_path);
fd = mkstemp(mpath);
if (fd < 0)
- die("Can't open %s for hugetlbfs map\n", mpath);
+ die("Can't open %s for hugetlbfs map", mpath);
unlink(mpath);
if (ftruncate(fd, size) < 0)
- die("Can't ftruncate for mem mapping size %lld\n",
+ die("Can't ftruncate for mem mapping size %lld",
(unsigned long long)size);
addr = mmap(NULL, size, PROT_RW, MAP_PRIVATE, fd, 0);
close(fd);
--
2.39.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration
2026-07-06 8:35 [PATCH kvmtool v1 0/5] Add guest_memfd support for arm64 Fuad Tabba
2026-07-06 8:35 ` [PATCH kvmtool v1 1/5] Initialize the return value in kvm__for_each_mem_bank() Fuad Tabba
2026-07-06 8:35 ` [PATCH kvmtool v1 2/5] Remove newline from end of die() aborts Fuad Tabba
@ 2026-07-06 8:35 ` Fuad Tabba
2026-07-06 10:23 ` Suzuki K Poulose
2026-07-06 8:35 ` [PATCH kvmtool v1 4/5] Add guest_memfd support to kvm__register_mem() Fuad Tabba
2026-07-06 8:35 ` [PATCH kvmtool v1 5/5] arm64: Add --guest-memfd option to back guest RAM with guest_memfd Fuad Tabba
4 siblings, 1 reply; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 8:35 UTC (permalink / raw)
To: kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Suzuki K Poulose,
Andre Przywara, Oliver Upton, Marc Zyngier, Fuad Tabba
Switch kvm__register_mem() and kvm__destroy_mem() from
kvm_userspace_memory_region to kvm_userspace_memory_region2 and
KVM_SET_USER_MEMORY_REGION2 unconditionally. The v2 struct is a
superset; when the extra fields are zero, the ioctl behaves
identically to v1.
This prepares for adding guest_memfd support.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
kvm.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/kvm.c b/kvm.c
index 95ccfcb..8a7fd09 100644
--- a/kvm.c
+++ b/kvm.c
@@ -189,7 +189,7 @@ core_exit(kvm__exit);
int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size,
void *userspace_addr)
{
- struct kvm_userspace_memory_region mem;
+ struct kvm_userspace_memory_region2 mem;
struct kvm_mem_bank *bank;
int ret;
@@ -213,14 +213,14 @@ int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size,
goto out;
}
- mem = (struct kvm_userspace_memory_region) {
+ mem = (struct kvm_userspace_memory_region2) {
.slot = bank->slot,
.guest_phys_addr = guest_phys,
.memory_size = 0,
.userspace_addr = (unsigned long)userspace_addr,
};
- ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
+ ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
if (ret < 0) {
ret = -errno;
goto out;
@@ -239,7 +239,6 @@ out:
int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
void *userspace_addr, enum kvm_mem_type type)
{
- struct kvm_userspace_memory_region mem;
struct kvm_mem_bank *merged = NULL;
struct kvm_mem_bank *bank;
struct list_head *prev_entry;
@@ -320,7 +319,7 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
flags |= KVM_MEM_READONLY;
if (type != KVM_MEM_TYPE_RESERVED) {
- mem = (struct kvm_userspace_memory_region) {
+ struct kvm_userspace_memory_region2 mem = {
.slot = slot,
.flags = flags,
.guest_phys_addr = guest_phys,
@@ -328,7 +327,7 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
.userspace_addr = (unsigned long)userspace_addr,
};
- ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
+ ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
if (ret < 0) {
ret = -errno;
goto out;
--
2.39.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH kvmtool v1 4/5] Add guest_memfd support to kvm__register_mem()
2026-07-06 8:35 [PATCH kvmtool v1 0/5] Add guest_memfd support for arm64 Fuad Tabba
` (2 preceding siblings ...)
2026-07-06 8:35 ` [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration Fuad Tabba
@ 2026-07-06 8:35 ` Fuad Tabba
2026-07-06 11:00 ` Suzuki K Poulose
2026-07-06 8:35 ` [PATCH kvmtool v1 5/5] arm64: Add --guest-memfd option to back guest RAM with guest_memfd Fuad Tabba
4 siblings, 1 reply; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 8:35 UTC (permalink / raw)
To: kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Suzuki K Poulose,
Andre Przywara, Oliver Upton, Marc Zyngier, Fuad Tabba
Add a guest_memfd file descriptor and offset to kvm__register_mem().
When guest_memfd >= 0, set KVM_MEM_GUEST_MEMFD and populate the
guest_memfd fields, binding the memslot to the file. This prepares
for backing guest RAM with guest_memfd on arm64.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
hw/cfi_flash.c | 2 +-
include/kvm/kvm.h | 15 +++++++++++++--
kvm.c | 9 ++++++++-
3 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/hw/cfi_flash.c b/hw/cfi_flash.c
index b68d9e0..4413955 100644
--- a/hw/cfi_flash.c
+++ b/hw/cfi_flash.c
@@ -451,7 +451,7 @@ static int map_flash_memory(struct kvm *kvm, struct cfi_flash_device *sfdev)
int ret;
ret = kvm__register_mem(kvm, sfdev->base_addr, sfdev->size,
- sfdev->flash_memory,
+ sfdev->flash_memory, -1, 0,
KVM_MEM_TYPE_RAM | KVM_MEM_TYPE_READONLY);
if (!ret)
sfdev->is_mapped = true;
diff --git a/include/kvm/kvm.h b/include/kvm/kvm.h
index eb23e2f..5e7b5eb 100644
--- a/include/kvm/kvm.h
+++ b/include/kvm/kvm.h
@@ -128,11 +128,22 @@ bool kvm__emulate_io(struct kvm_cpu *vcpu, u16 port, void *data, int direction,
bool kvm__emulate_mmio(struct kvm_cpu *vcpu, u64 phys_addr, u8 *data, u32 len, u8 is_write);
int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size, void *userspace_addr);
int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size, void *userspace_addr,
+ int guest_memfd, u64 guest_memfd_offset,
enum kvm_mem_type type);
static inline int kvm__register_ram(struct kvm *kvm, u64 guest_phys, u64 size,
void *userspace_addr)
{
return kvm__register_mem(kvm, guest_phys, size, userspace_addr,
+ -1, 0, KVM_MEM_TYPE_RAM);
+}
+
+static inline int kvm__register_ram_guest_memfd(struct kvm *kvm, u64 guest_phys,
+ u64 size, void *userspace_addr,
+ int guest_memfd,
+ u64 guest_memfd_offset)
+{
+ return kvm__register_mem(kvm, guest_phys, size, userspace_addr,
+ guest_memfd, guest_memfd_offset,
KVM_MEM_TYPE_RAM);
}
@@ -140,13 +151,13 @@ static inline int kvm__register_dev_mem(struct kvm *kvm, u64 guest_phys,
u64 size, void *userspace_addr)
{
return kvm__register_mem(kvm, guest_phys, size, userspace_addr,
- KVM_MEM_TYPE_DEVICE);
+ -1, 0, KVM_MEM_TYPE_DEVICE);
}
static inline int kvm__reserve_mem(struct kvm *kvm, u64 guest_phys, u64 size)
{
return kvm__register_mem(kvm, guest_phys, size, NULL,
- KVM_MEM_TYPE_RESERVED);
+ -1, 0, KVM_MEM_TYPE_RESERVED);
}
int __must_check kvm__register_iotrap(struct kvm *kvm, u64 phys_addr, u64 len,
diff --git a/kvm.c b/kvm.c
index 8a7fd09..3d05756 100644
--- a/kvm.c
+++ b/kvm.c
@@ -237,7 +237,8 @@ out:
}
int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
- void *userspace_addr, enum kvm_mem_type type)
+ void *userspace_addr, int guest_memfd,
+ u64 guest_memfd_offset, enum kvm_mem_type type)
{
struct kvm_mem_bank *merged = NULL;
struct kvm_mem_bank *bank;
@@ -327,6 +328,12 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
.userspace_addr = (unsigned long)userspace_addr,
};
+ if (guest_memfd >= 0) {
+ mem.flags |= KVM_MEM_GUEST_MEMFD;
+ mem.guest_memfd = guest_memfd;
+ mem.guest_memfd_offset = guest_memfd_offset;
+ }
+
ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
if (ret < 0) {
ret = -errno;
--
2.39.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH kvmtool v1 5/5] arm64: Add --guest-memfd option to back guest RAM with guest_memfd
2026-07-06 8:35 [PATCH kvmtool v1 0/5] Add guest_memfd support for arm64 Fuad Tabba
` (3 preceding siblings ...)
2026-07-06 8:35 ` [PATCH kvmtool v1 4/5] Add guest_memfd support to kvm__register_mem() Fuad Tabba
@ 2026-07-06 8:35 ` Fuad Tabba
2026-07-06 11:18 ` Suzuki K Poulose
4 siblings, 1 reply; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 8:35 UTC (permalink / raw)
To: kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Suzuki K Poulose,
Andre Przywara, Oliver Upton, Marc Zyngier, Fuad Tabba
Back guest RAM with a guest_memfd created with GUEST_MEMFD_FLAG_MMAP
and GUEST_MEMFD_FLAG_INIT_SHARED, mmapped MAP_SHARED to provide the
userspace mapping.
Protected VMs need in-place shared/private conversion of guest_memfd,
which the kernel does not support yet, so reject the combination of
--protected and --guest-memfd.
Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
---
arm64/include/kvm/kvm-config-arch.h | 5 +++-
arm64/kvm.c | 38 +++++++++++++++++++++++------
include/kvm/kvm.h | 1 +
include/kvm/util.h | 1 +
kvm.c | 1 +
util/util.c | 34 ++++++++++++++++++++++++++
6 files changed, 71 insertions(+), 9 deletions(-)
diff --git a/arm64/include/kvm/kvm-config-arch.h b/arm64/include/kvm/kvm-config-arch.h
index c2702d5..d8a8ef7 100644
--- a/arm64/include/kvm/kvm-config-arch.h
+++ b/arm64/include/kvm/kvm-config-arch.h
@@ -20,6 +20,7 @@ struct kvm_config_arch {
bool no_pvtime;
bool psci;
bool protected;
+ bool guest_memfd;
};
int irqchip_parser(const struct option *opt, const char *arg, int unset);
@@ -73,6 +74,8 @@ int sve_vl_parser(const struct option *opt, const char *arg, int unset);
OPT_BOOLEAN('\0', "e2h0", &(cfg)->e2h0, \
"Create guest without VHE support"), \
OPT_BOOLEAN('\0', "protected", &(cfg)->protected, \
- "Create a protected VM when pKVM is enabled"),
+ "Create a protected VM when pKVM is enabled"), \
+ OPT_BOOLEAN('\0', "guest-memfd", &(cfg)->guest_memfd, \
+ "Use guest_memfd to back guest RAM"),
#endif /* ARM_COMMON__KVM_CONFIG_ARCH_H */
diff --git a/arm64/kvm.c b/arm64/kvm.c
index fb0b98d..b594578 100644
--- a/arm64/kvm.c
+++ b/arm64/kvm.c
@@ -49,9 +49,14 @@ void kvm__init_ram(struct kvm *kvm)
kvm->arch.ram_alloc_size = kvm->ram_size;
if (!kvm->cfg.hugetlbfs_path)
kvm->arch.ram_alloc_size += SZ_2M;
- kvm->arch.ram_alloc_start = mmap_anon_or_hugetlbfs(kvm,
- kvm->cfg.hugetlbfs_path,
- kvm->arch.ram_alloc_size);
+
+ if (kvm->cfg.arch.guest_memfd)
+ kvm->arch.ram_alloc_start =
+ mmap_guest_memfd(kvm, kvm->arch.ram_alloc_size);
+ else
+ kvm->arch.ram_alloc_start =
+ mmap_anon_or_hugetlbfs(kvm, kvm->cfg.hugetlbfs_path,
+ kvm->arch.ram_alloc_size);
if (kvm->arch.ram_alloc_start == MAP_FAILED)
die("Failed to map %lld bytes for guest memory (%d)",
@@ -60,17 +65,25 @@ void kvm__init_ram(struct kvm *kvm)
kvm->ram_start = (void *)ALIGN((unsigned long)kvm->arch.ram_alloc_start,
SZ_2M);
- madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
- MADV_MERGEABLE);
+ if (!kvm->cfg.arch.guest_memfd) {
+ madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
+ MADV_MERGEABLE);
- madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
- MADV_HUGEPAGE);
+ madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
+ MADV_HUGEPAGE);
+ }
phys_start = kvm->cfg.ram_addr;
phys_size = kvm->ram_size;
host_mem = kvm->ram_start;
- err = kvm__register_ram(kvm, phys_start, phys_size, host_mem);
+ if (kvm->cfg.arch.guest_memfd)
+ err = kvm__register_ram_guest_memfd(kvm, phys_start,
+ phys_size, host_mem,
+ kvm->ram_guest_memfd,
+ host_mem - kvm->arch.ram_alloc_start);
+ else
+ err = kvm__register_ram(kvm, phys_start, phys_size, host_mem);
if (err)
die("Failed to register %lld bytes of memory at physical "
"address 0x%llx [err %d]", phys_size, phys_start, err);
@@ -468,6 +481,15 @@ void kvm__arch_validate_cfg(struct kvm *kvm)
if (kvm->cfg.arch.e2h0 && !kvm->cfg.arch.nested_virt)
pr_warning("--e2h0 requires --nested, ignoring");
+ if (kvm->cfg.arch.guest_memfd) {
+ /* Requires in-place shared/private conversion of guest_memfd. */
+ if (kvm->cfg.arch.protected)
+ die("Protected VMs do not yet support guest_memfd");
+
+ if (kvm->cfg.hugetlbfs_path)
+ die("guest_memfd cannot be backed by hugetlbfs");
+ }
+
if (kvm->cfg.arch.protected) {
if (kvm->cfg.ram_size &&
kvm->cfg.ram_size < DMA_MEM_REGION_SIZE) {
diff --git a/include/kvm/kvm.h b/include/kvm/kvm.h
index 5e7b5eb..95dc52f 100644
--- a/include/kvm/kvm.h
+++ b/include/kvm/kvm.h
@@ -90,6 +90,7 @@ struct kvm {
u64 ram_size; /* Guest memory size, in bytes */
void *ram_start;
u64 ram_pagesize;
+ int ram_guest_memfd; /* guest_memfd backing RAM, -1 if none */
struct mutex mem_banks_lock;
struct list_head mem_banks;
diff --git a/include/kvm/util.h b/include/kvm/util.h
index a6dba11..9e23431 100644
--- a/include/kvm/util.h
+++ b/include/kvm/util.h
@@ -148,5 +148,6 @@ static inline int pow2_size(unsigned long x)
struct kvm;
void *mmap_hugetlbfs(struct kvm *kvm, const char *htlbfs_path, u64 size);
void *mmap_anon_or_hugetlbfs(struct kvm *kvm, const char *hugetlbfs_path, u64 size);
+void *mmap_guest_memfd(struct kvm *kvm, u64 size);
#endif /* KVM__UTIL_H */
diff --git a/kvm.c b/kvm.c
index 3d05756..a400dd2 100644
--- a/kvm.c
+++ b/kvm.c
@@ -162,6 +162,7 @@ struct kvm *kvm__new(void)
mutex_init(&kvm->mem_banks_lock);
kvm->sys_fd = -1;
kvm->vm_fd = -1;
+ kvm->ram_guest_memfd = -1;
#ifdef KVM_BRLOCK_DEBUG
kvm->brlock_sem = (pthread_rwlock_t) PTHREAD_RWLOCK_INITIALIZER;
diff --git a/util/util.c b/util/util.c
index 05d49c4..c60fe02 100644
--- a/util/util.c
+++ b/util/util.c
@@ -6,7 +6,9 @@
#include "kvm/util.h"
#include <kvm/kvm.h>
+#include <linux/kvm.h>
#include <linux/magic.h> /* For HUGETLBFS_MAGIC */
+#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/statfs.h>
@@ -169,3 +171,35 @@ void *mmap_anon_or_hugetlbfs(struct kvm *kvm, const char *hugetlbfs_path, u64 si
return mmap(NULL, size, PROT_RW, MAP_ANON_NORESERVE, -1, 0);
}
}
+
+/*
+ * Map guest RAM backed by a guest_memfd. The file descriptor must stay open
+ * for the lifetime of the VM: closing it invalidates the memslot bindings.
+ */
+void *mmap_guest_memfd(struct kvm *kvm, u64 size)
+{
+ u64 flags = GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_INIT_SHARED;
+ struct kvm_create_guest_memfd gmem = {
+ .size = size,
+ .flags = flags,
+ };
+ void *addr;
+ int ret;
+
+ ret = ioctl(kvm->vm_fd, KVM_CHECK_EXTENSION, KVM_CAP_GUEST_MEMFD_FLAGS);
+ if (ret < 0 || ((u64)ret & flags) != flags)
+ die("kernel does not support guest_memfd with mmap");
+
+ ret = ioctl(kvm->vm_fd, KVM_CREATE_GUEST_MEMFD, &gmem);
+ if (ret < 0)
+ die_perror("KVM_CREATE_GUEST_MEMFD");
+
+ addr = mmap(NULL, size, PROT_RW, MAP_SHARED, ret, 0);
+ if (addr == MAP_FAILED)
+ die_perror("guest_memfd mmap");
+
+ kvm->ram_guest_memfd = ret;
+ kvm->ram_pagesize = getpagesize();
+
+ return addr;
+}
--
2.39.5
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration
2026-07-06 8:35 ` [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration Fuad Tabba
@ 2026-07-06 10:23 ` Suzuki K Poulose
2026-07-06 10:41 ` Fuad Tabba
2026-07-06 10:57 ` Alexandru Elisei
0 siblings, 2 replies; 20+ messages in thread
From: Suzuki K Poulose @ 2026-07-06 10:23 UTC (permalink / raw)
To: Fuad Tabba, kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier, Fuad Tabba
Hi Fuad
On 06/07/2026 09:35, Fuad Tabba wrote:
> Switch kvm__register_mem() and kvm__destroy_mem() from
> kvm_userspace_memory_region to kvm_userspace_memory_region2 and
> KVM_SET_USER_MEMORY_REGION2 unconditionally. The v2 struct is a
> superset; when the extra fields are zero, the ioctl behaves
> identically to v1.
Doesn't this break using this on older KVM implementations ? Not that I
care, but do others care ?
Cheers
Suzuki
>
> This prepares for adding guest_memfd support.
>
> Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
> ---
> kvm.c | 11 +++++------
> 1 file changed, 5 insertions(+), 6 deletions(-)
>
> diff --git a/kvm.c b/kvm.c
> index 95ccfcb..8a7fd09 100644
> --- a/kvm.c
> +++ b/kvm.c
> @@ -189,7 +189,7 @@ core_exit(kvm__exit);
> int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> void *userspace_addr)
> {
> - struct kvm_userspace_memory_region mem;
> + struct kvm_userspace_memory_region2 mem;
> struct kvm_mem_bank *bank;
> int ret;
>
> @@ -213,14 +213,14 @@ int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> goto out;
> }
>
> - mem = (struct kvm_userspace_memory_region) {
> + mem = (struct kvm_userspace_memory_region2) {
> .slot = bank->slot,
> .guest_phys_addr = guest_phys,
> .memory_size = 0,
> .userspace_addr = (unsigned long)userspace_addr,
> };
>
> - ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
> + ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
> if (ret < 0) {
> ret = -errno;
> goto out;
> @@ -239,7 +239,6 @@ out:
> int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> void *userspace_addr, enum kvm_mem_type type)
> {
> - struct kvm_userspace_memory_region mem;
> struct kvm_mem_bank *merged = NULL;
> struct kvm_mem_bank *bank;
> struct list_head *prev_entry;
> @@ -320,7 +319,7 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> flags |= KVM_MEM_READONLY;
>
> if (type != KVM_MEM_TYPE_RESERVED) {
> - mem = (struct kvm_userspace_memory_region) {
> + struct kvm_userspace_memory_region2 mem = {
> .slot = slot,
> .flags = flags,
> .guest_phys_addr = guest_phys,
> @@ -328,7 +327,7 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> .userspace_addr = (unsigned long)userspace_addr,
> };
>
> - ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
> + ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
> if (ret < 0) {
> ret = -errno;
> goto out;
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration
2026-07-06 10:23 ` Suzuki K Poulose
@ 2026-07-06 10:41 ` Fuad Tabba
2026-07-06 10:46 ` Will Deacon
2026-07-06 10:57 ` Alexandru Elisei
1 sibling, 1 reply; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 10:41 UTC (permalink / raw)
To: Suzuki K Poulose
Cc: kvm, kvmarm, Will Deacon, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier
Hi Suzuki,
On Mon, 6 Jul 2026 at 11:25, Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>
> Hi Fuad
>
> On 06/07/2026 09:35, Fuad Tabba wrote:
> > Switch kvm__register_mem() and kvm__destroy_mem() from
> > kvm_userspace_memory_region to kvm_userspace_memory_region2 and
> > KVM_SET_USER_MEMORY_REGION2 unconditionally. The v2 struct is a
> > superset; when the extra fields are zero, the ioctl behaves
> > identically to v1.
>
> Doesn't this break using this on older KVM implementations ? Not that I
> care, but do others care ?
KVM_SET_USER_MEMORY_REGION2 goes back to 6.8, so this would break
kernels older than that. I can gate it so that REGION2 is only used
when --guest-memfd is requested, keeping REGION for the default path.
But like you said, do others care for kernels that old?
Cheers,
/fuad
>
> Cheers
> Suzuki
>
>
> >
> > This prepares for adding guest_memfd support.
> >
> > Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
> > ---
> > kvm.c | 11 +++++------
> > 1 file changed, 5 insertions(+), 6 deletions(-)
> >
> > diff --git a/kvm.c b/kvm.c
> > index 95ccfcb..8a7fd09 100644
> > --- a/kvm.c
> > +++ b/kvm.c
> > @@ -189,7 +189,7 @@ core_exit(kvm__exit);
> > int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > void *userspace_addr)
> > {
> > - struct kvm_userspace_memory_region mem;
> > + struct kvm_userspace_memory_region2 mem;
> > struct kvm_mem_bank *bank;
> > int ret;
> >
> > @@ -213,14 +213,14 @@ int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > goto out;
> > }
> >
> > - mem = (struct kvm_userspace_memory_region) {
> > + mem = (struct kvm_userspace_memory_region2) {
> > .slot = bank->slot,
> > .guest_phys_addr = guest_phys,
> > .memory_size = 0,
> > .userspace_addr = (unsigned long)userspace_addr,
> > };
> >
> > - ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
> > + ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
> > if (ret < 0) {
> > ret = -errno;
> > goto out;
> > @@ -239,7 +239,6 @@ out:
> > int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > void *userspace_addr, enum kvm_mem_type type)
> > {
> > - struct kvm_userspace_memory_region mem;
> > struct kvm_mem_bank *merged = NULL;
> > struct kvm_mem_bank *bank;
> > struct list_head *prev_entry;
> > @@ -320,7 +319,7 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > flags |= KVM_MEM_READONLY;
> >
> > if (type != KVM_MEM_TYPE_RESERVED) {
> > - mem = (struct kvm_userspace_memory_region) {
> > + struct kvm_userspace_memory_region2 mem = {
> > .slot = slot,
> > .flags = flags,
> > .guest_phys_addr = guest_phys,
> > @@ -328,7 +327,7 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > .userspace_addr = (unsigned long)userspace_addr,
> > };
> >
> > - ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
> > + ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
> > if (ret < 0) {
> > ret = -errno;
> > goto out;
>
> IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration
2026-07-06 10:41 ` Fuad Tabba
@ 2026-07-06 10:46 ` Will Deacon
2026-07-06 10:47 ` Fuad Tabba
0 siblings, 1 reply; 20+ messages in thread
From: Will Deacon @ 2026-07-06 10:46 UTC (permalink / raw)
To: Fuad Tabba
Cc: Suzuki K Poulose, kvm, kvmarm, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier
On Mon, Jul 06, 2026 at 11:41:21AM +0100, Fuad Tabba wrote:
> Hi Suzuki,
>
> On Mon, 6 Jul 2026 at 11:25, Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
> >
> > Hi Fuad
> >
> > On 06/07/2026 09:35, Fuad Tabba wrote:
> > > Switch kvm__register_mem() and kvm__destroy_mem() from
> > > kvm_userspace_memory_region to kvm_userspace_memory_region2 and
> > > KVM_SET_USER_MEMORY_REGION2 unconditionally. The v2 struct is a
> > > superset; when the extra fields are zero, the ioctl behaves
> > > identically to v1.
> >
> > Doesn't this break using this on older KVM implementations ? Not that I
> > care, but do others care ?
>
> KVM_SET_USER_MEMORY_REGION2 goes back to 6.8, so this would break
> kernels older than that. I can gate it so that REGION2 is only used
> when --guest-memfd is requested, keeping REGION for the default path.
> But like you said, do others care for kernels that old?
v6.8 isn't that old, please don't break it ;)
Will
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration
2026-07-06 10:46 ` Will Deacon
@ 2026-07-06 10:47 ` Fuad Tabba
0 siblings, 0 replies; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 10:47 UTC (permalink / raw)
To: Will Deacon
Cc: Suzuki K Poulose, kvm, kvmarm, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier
On Mon, 6 Jul 2026 at 11:46, Will Deacon <will@kernel.org> wrote:
>
> On Mon, Jul 06, 2026 at 11:41:21AM +0100, Fuad Tabba wrote:
> > Hi Suzuki,
> >
> > On Mon, 6 Jul 2026 at 11:25, Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
> > >
> > > Hi Fuad
> > >
> > > On 06/07/2026 09:35, Fuad Tabba wrote:
> > > > Switch kvm__register_mem() and kvm__destroy_mem() from
> > > > kvm_userspace_memory_region to kvm_userspace_memory_region2 and
> > > > KVM_SET_USER_MEMORY_REGION2 unconditionally. The v2 struct is a
> > > > superset; when the extra fields are zero, the ioctl behaves
> > > > identically to v1.
> > >
> > > Doesn't this break using this on older KVM implementations ? Not that I
> > > care, but do others care ?
> >
> > KVM_SET_USER_MEMORY_REGION2 goes back to 6.8, so this would break
> > kernels older than that. I can gate it so that REGION2 is only used
> > when --guest-memfd is requested, keeping REGION for the default path.
> > But like you said, do others care for kernels that old?
>
> v6.8 isn't that old, please don't break it ;)
Sure. I'll fix that in V2 :)
Cheers,
/fuad
>
> Will
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 2/5] Remove newline from end of die() aborts
2026-07-06 8:35 ` [PATCH kvmtool v1 2/5] Remove newline from end of die() aborts Fuad Tabba
@ 2026-07-06 10:53 ` Suzuki K Poulose
2026-07-06 10:57 ` Fuad Tabba
0 siblings, 1 reply; 20+ messages in thread
From: Suzuki K Poulose @ 2026-07-06 10:53 UTC (permalink / raw)
To: Fuad Tabba, kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier, Fuad Tabba
On 06/07/2026 09:35, Fuad Tabba wrote:
> die() already adds the newline at the end of the string.
>
> Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
While you are at it, there are plenty more die() with \n, worth fixing
them too ?
$ git grep "die(" | grep "\\\n" | grep -v util
arm64/kvm-cpu.c: die("32bit guests are not
supported\n");
arm64/kvm-cpu.c: die("What's that mode???\n");
builtin-run.c: die("Unable to resolve program %s:
%s\n", src, strerror(errno));
builtin-run.c: die("Pathname too long: %s -> %s\n",
src, resolved_path);
include/kvm/fdt.h: die("Error creating device tree:
%s: %s\n", \
kvm-cpu.c: die("Pause signaled for already paused
CPU\n");
kvm.c: die("kvm__arch_setup_firmware() failed with
error %d\n", ret);
powerpc/kvm.c: die("Couldn't map %lld bytes for RAM (%d)\n",
powerpc/kvm.c: die("Can't allocate %d bytes for HPT\n",
(1<<HPT_ORDER));
powerpc/kvm.c: die("initrd too big to contain
in guest RAM.\n");
powerpc/kvm.c: die("Couldn't create RTAS FDT properties\n");
powerpc/spapr_hvcons.c: die("term_putc_iov error %d!\n",
errno);
powerpc/spapr_pci.c: die("error making bus subnode, %s\n",
fdt_strerror(bus_off));
powerpc/spapr_pci.c: die("error making node subnode,
%s\n", fdt_strerror(bus_off));
powerpc/xics.c: die("Invalid server number for cpuid
%ld\n", vcpu->cpu_id);
riscv/fdt.c: die("Invalid CPU type %s\n", arg);
riscv/fdt.c: die("No way to generate IRQCHIP FDT node\n");
riscv/irq.c: die("No IRQCHIP found\n");
term.c: die("term_poll_thread_loop: error polling device fds %d\n", errno);
term.c: die("Unable to create console input poll thread\n");
virtio/mmio.c: die("Unable to generate device tree nodes without
libfdt\n");
virtio/vhost.c: die("Unable to start vhost polling thread\n");
x86/boot.c: die("firmware image %s is too big to fit in
memory (%Lu KB).\n", firmware_filename, (u64)(st.st_size / 1024));
> ---
> util/util.c | 10 +++++-----
> 1 file changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/util/util.c b/util/util.c
> index 6f41e2c..05d49c4 100644
> --- a/util/util.c
> +++ b/util/util.c
> @@ -128,14 +128,14 @@ void *mmap_hugetlbfs(struct kvm *kvm, const char *htlbfs_path, u64 size)
> unsigned long blk_size;
>
> if (statfs(htlbfs_path, &sfs) < 0)
> - die("Can't stat %s\n", htlbfs_path);
> + die("Can't stat %s", htlbfs_path);
>
> if ((unsigned int)sfs.f_type != HUGETLBFS_MAGIC)
> - die("%s is not hugetlbfs!\n", htlbfs_path);
> + die("%s is not hugetlbfs!", htlbfs_path);
>
> blk_size = (unsigned long)sfs.f_bsize;
> if (sfs.f_bsize == 0 || blk_size > size) {
> - die("Can't use hugetlbfs pagesize %ld for mem size %lld\n",
> + die("Can't use hugetlbfs pagesize %ld for mem size %lld",
> blk_size, (unsigned long long)size);
> }
>
> @@ -144,10 +144,10 @@ void *mmap_hugetlbfs(struct kvm *kvm, const char *htlbfs_path, u64 size)
> snprintf(mpath, PATH_MAX, "%s/kvmtoolXXXXXX", htlbfs_path);
> fd = mkstemp(mpath);
> if (fd < 0)
> - die("Can't open %s for hugetlbfs map\n", mpath);
> + die("Can't open %s for hugetlbfs map", mpath);
> unlink(mpath);
> if (ftruncate(fd, size) < 0)
> - die("Can't ftruncate for mem mapping size %lld\n",
> + die("Can't ftruncate for mem mapping size %lld",
> (unsigned long long)size);
> addr = mmap(NULL, size, PROT_RW, MAP_PRIVATE, fd, 0);
> close(fd);
For this one :
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration
2026-07-06 10:23 ` Suzuki K Poulose
2026-07-06 10:41 ` Fuad Tabba
@ 2026-07-06 10:57 ` Alexandru Elisei
2026-07-06 10:59 ` Fuad Tabba
1 sibling, 1 reply; 20+ messages in thread
From: Alexandru Elisei @ 2026-07-06 10:57 UTC (permalink / raw)
To: Suzuki K Poulose
Cc: Fuad Tabba, kvm, kvmarm, Will Deacon, Andre Przywara,
Oliver Upton, Marc Zyngier, Fuad Tabba
Hi Fuad, Suzuki,
On Mon, Jul 06, 2026 at 11:23:47AM +0100, Suzuki K Poulose wrote:
> Hi Fuad
>
> On 06/07/2026 09:35, Fuad Tabba wrote:
> > Switch kvm__register_mem() and kvm__destroy_mem() from
> > kvm_userspace_memory_region to kvm_userspace_memory_region2 and
> > KVM_SET_USER_MEMORY_REGION2 unconditionally. The v2 struct is a
> > superset; when the extra fields are zero, the ioctl behaves
> > identically to v1.
>
> Doesn't this break using this on older KVM implementations ? Not that I
> care, but do others care ?
I haven't had the time to review the series, but I do care about this
change, I find myself from time to time testing old kernels.
I think the two ioctls can coexist without much maintenance burden. For
example, the usage of KVM_SET_USER_MEMORY_REGION can be kept as-is and
KVM_SET_USER_MEMORY_REGION2 can be used only for guest_memfd in
kvm__register_mem().
Thoughts?
Thanks,
Alex
>
> Cheers
> Suzuki
>
>
> >
> > This prepares for adding guest_memfd support.
> >
> > Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
> > ---
> > kvm.c | 11 +++++------
> > 1 file changed, 5 insertions(+), 6 deletions(-)
> >
> > diff --git a/kvm.c b/kvm.c
> > index 95ccfcb..8a7fd09 100644
> > --- a/kvm.c
> > +++ b/kvm.c
> > @@ -189,7 +189,7 @@ core_exit(kvm__exit);
> > int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > void *userspace_addr)
> > {
> > - struct kvm_userspace_memory_region mem;
> > + struct kvm_userspace_memory_region2 mem;
> > struct kvm_mem_bank *bank;
> > int ret;
> > @@ -213,14 +213,14 @@ int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > goto out;
> > }
> > - mem = (struct kvm_userspace_memory_region) {
> > + mem = (struct kvm_userspace_memory_region2) {
> > .slot = bank->slot,
> > .guest_phys_addr = guest_phys,
> > .memory_size = 0,
> > .userspace_addr = (unsigned long)userspace_addr,
> > };
> > - ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
> > + ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
> > if (ret < 0) {
> > ret = -errno;
> > goto out;
> > @@ -239,7 +239,6 @@ out:
> > int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > void *userspace_addr, enum kvm_mem_type type)
> > {
> > - struct kvm_userspace_memory_region mem;
> > struct kvm_mem_bank *merged = NULL;
> > struct kvm_mem_bank *bank;
> > struct list_head *prev_entry;
> > @@ -320,7 +319,7 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > flags |= KVM_MEM_READONLY;
> > if (type != KVM_MEM_TYPE_RESERVED) {
> > - mem = (struct kvm_userspace_memory_region) {
> > + struct kvm_userspace_memory_region2 mem = {
> > .slot = slot,
> > .flags = flags,
> > .guest_phys_addr = guest_phys,
> > @@ -328,7 +327,7 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > .userspace_addr = (unsigned long)userspace_addr,
> > };
> > - ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
> > + ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
> > if (ret < 0) {
> > ret = -errno;
> > goto out;
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 2/5] Remove newline from end of die() aborts
2026-07-06 10:53 ` Suzuki K Poulose
@ 2026-07-06 10:57 ` Fuad Tabba
0 siblings, 0 replies; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 10:57 UTC (permalink / raw)
To: Suzuki K Poulose
Cc: kvm, kvmarm, Will Deacon, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier
On Mon, 6 Jul 2026 at 11:54, Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>
> On 06/07/2026 09:35, Fuad Tabba wrote:
> > die() already adds the newline at the end of the string.
> >
> > Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
>
> While you are at it, there are plenty more die() with \n, worth fixing
> them too ?
I fixed the ones relevant to this series. I'm happy to do the rest, if
Will doesn't mind the churn. One commit or multiple ones?
Thanks for the review!
Cheers,
/fuad
>
> $ git grep "die(" | grep "\\\n" | grep -v util
> arm64/kvm-cpu.c: die("32bit guests are not
> supported\n");
> arm64/kvm-cpu.c: die("What's that mode???\n");
> builtin-run.c: die("Unable to resolve program %s:
> %s\n", src, strerror(errno));
> builtin-run.c: die("Pathname too long: %s -> %s\n",
> src, resolved_path);
> include/kvm/fdt.h: die("Error creating device tree:
> %s: %s\n", \
> kvm-cpu.c: die("Pause signaled for already paused
> CPU\n");
> kvm.c: die("kvm__arch_setup_firmware() failed with
> error %d\n", ret);
> powerpc/kvm.c: die("Couldn't map %lld bytes for RAM (%d)\n",
> powerpc/kvm.c: die("Can't allocate %d bytes for HPT\n",
> (1<<HPT_ORDER));
> powerpc/kvm.c: die("initrd too big to contain
> in guest RAM.\n");
> powerpc/kvm.c: die("Couldn't create RTAS FDT properties\n");
> powerpc/spapr_hvcons.c: die("term_putc_iov error %d!\n",
> errno);
> powerpc/spapr_pci.c: die("error making bus subnode, %s\n",
> fdt_strerror(bus_off));
> powerpc/spapr_pci.c: die("error making node subnode,
> %s\n", fdt_strerror(bus_off));
> powerpc/xics.c: die("Invalid server number for cpuid
> %ld\n", vcpu->cpu_id);
> riscv/fdt.c: die("Invalid CPU type %s\n", arg);
> riscv/fdt.c: die("No way to generate IRQCHIP FDT node\n");
> riscv/irq.c: die("No IRQCHIP found\n");
> term.c: die("term_poll_thread_loop: error polling device fds %d\n", errno);
> term.c: die("Unable to create console input poll thread\n");
> virtio/mmio.c: die("Unable to generate device tree nodes without
> libfdt\n");
> virtio/vhost.c: die("Unable to start vhost polling thread\n");
> x86/boot.c: die("firmware image %s is too big to fit in
> memory (%Lu KB).\n", firmware_filename, (u64)(st.st_size / 1024));
>
>
> > ---
> > util/util.c | 10 +++++-----
> > 1 file changed, 5 insertions(+), 5 deletions(-)
> >
> > diff --git a/util/util.c b/util/util.c
> > index 6f41e2c..05d49c4 100644
> > --- a/util/util.c
> > +++ b/util/util.c
> > @@ -128,14 +128,14 @@ void *mmap_hugetlbfs(struct kvm *kvm, const char *htlbfs_path, u64 size)
> > unsigned long blk_size;
> >
> > if (statfs(htlbfs_path, &sfs) < 0)
> > - die("Can't stat %s\n", htlbfs_path);
> > + die("Can't stat %s", htlbfs_path);
> >
> > if ((unsigned int)sfs.f_type != HUGETLBFS_MAGIC)
> > - die("%s is not hugetlbfs!\n", htlbfs_path);
> > + die("%s is not hugetlbfs!", htlbfs_path);
> >
> > blk_size = (unsigned long)sfs.f_bsize;
> > if (sfs.f_bsize == 0 || blk_size > size) {
> > - die("Can't use hugetlbfs pagesize %ld for mem size %lld\n",
> > + die("Can't use hugetlbfs pagesize %ld for mem size %lld",
> > blk_size, (unsigned long long)size);
> > }
> >
> > @@ -144,10 +144,10 @@ void *mmap_hugetlbfs(struct kvm *kvm, const char *htlbfs_path, u64 size)
> > snprintf(mpath, PATH_MAX, "%s/kvmtoolXXXXXX", htlbfs_path);
> > fd = mkstemp(mpath);
> > if (fd < 0)
> > - die("Can't open %s for hugetlbfs map\n", mpath);
> > + die("Can't open %s for hugetlbfs map", mpath);
> > unlink(mpath);
> > if (ftruncate(fd, size) < 0)
> > - die("Can't ftruncate for mem mapping size %lld\n",
> > + die("Can't ftruncate for mem mapping size %lld",
> > (unsigned long long)size);
> > addr = mmap(NULL, size, PROT_RW, MAP_PRIVATE, fd, 0);
> > close(fd);
>
> For this one :
>
> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration
2026-07-06 10:57 ` Alexandru Elisei
@ 2026-07-06 10:59 ` Fuad Tabba
0 siblings, 0 replies; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 10:59 UTC (permalink / raw)
To: Alexandru Elisei
Cc: Suzuki K Poulose, kvm, kvmarm, Will Deacon, Andre Przywara,
Oliver Upton, Marc Zyngier
On Mon, 6 Jul 2026 at 11:57, Alexandru Elisei <alexandru.elisei@arm.com> wrote:
>
> Hi Fuad, Suzuki,
>
> On Mon, Jul 06, 2026 at 11:23:47AM +0100, Suzuki K Poulose wrote:
> > Hi Fuad
> >
> > On 06/07/2026 09:35, Fuad Tabba wrote:
> > > Switch kvm__register_mem() and kvm__destroy_mem() from
> > > kvm_userspace_memory_region to kvm_userspace_memory_region2 and
> > > KVM_SET_USER_MEMORY_REGION2 unconditionally. The v2 struct is a
> > > superset; when the extra fields are zero, the ioctl behaves
> > > identically to v1.
> >
> > Doesn't this break using this on older KVM implementations ? Not that I
> > care, but do others care ?
>
> I haven't had the time to review the series, but I do care about this
> change, I find myself from time to time testing old kernels.
>
> I think the two ioctls can coexist without much maintenance burden. For
> example, the usage of KVM_SET_USER_MEMORY_REGION can be kept as-is and
> KVM_SET_USER_MEMORY_REGION2 can be used only for guest_memfd in
> kvm__register_mem().
>
> Thoughts?
Already did that. I'll wait for more reviews before posting V2 with this change.
Cheers,
/fuad
>
> Thanks,
> Alex
>
> >
> > Cheers
> > Suzuki
> >
> >
> > >
> > > This prepares for adding guest_memfd support.
> > >
> > > Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
> > > ---
> > > kvm.c | 11 +++++------
> > > 1 file changed, 5 insertions(+), 6 deletions(-)
> > >
> > > diff --git a/kvm.c b/kvm.c
> > > index 95ccfcb..8a7fd09 100644
> > > --- a/kvm.c
> > > +++ b/kvm.c
> > > @@ -189,7 +189,7 @@ core_exit(kvm__exit);
> > > int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > > void *userspace_addr)
> > > {
> > > - struct kvm_userspace_memory_region mem;
> > > + struct kvm_userspace_memory_region2 mem;
> > > struct kvm_mem_bank *bank;
> > > int ret;
> > > @@ -213,14 +213,14 @@ int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > > goto out;
> > > }
> > > - mem = (struct kvm_userspace_memory_region) {
> > > + mem = (struct kvm_userspace_memory_region2) {
> > > .slot = bank->slot,
> > > .guest_phys_addr = guest_phys,
> > > .memory_size = 0,
> > > .userspace_addr = (unsigned long)userspace_addr,
> > > };
> > > - ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
> > > + ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
> > > if (ret < 0) {
> > > ret = -errno;
> > > goto out;
> > > @@ -239,7 +239,6 @@ out:
> > > int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > > void *userspace_addr, enum kvm_mem_type type)
> > > {
> > > - struct kvm_userspace_memory_region mem;
> > > struct kvm_mem_bank *merged = NULL;
> > > struct kvm_mem_bank *bank;
> > > struct list_head *prev_entry;
> > > @@ -320,7 +319,7 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > > flags |= KVM_MEM_READONLY;
> > > if (type != KVM_MEM_TYPE_RESERVED) {
> > > - mem = (struct kvm_userspace_memory_region) {
> > > + struct kvm_userspace_memory_region2 mem = {
> > > .slot = slot,
> > > .flags = flags,
> > > .guest_phys_addr = guest_phys,
> > > @@ -328,7 +327,7 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> > > .userspace_addr = (unsigned long)userspace_addr,
> > > };
> > > - ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION, &mem);
> > > + ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
> > > if (ret < 0) {
> > > ret = -errno;
> > > goto out;
> >
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 4/5] Add guest_memfd support to kvm__register_mem()
2026-07-06 8:35 ` [PATCH kvmtool v1 4/5] Add guest_memfd support to kvm__register_mem() Fuad Tabba
@ 2026-07-06 11:00 ` Suzuki K Poulose
2026-07-06 11:13 ` Suzuki K Poulose
0 siblings, 1 reply; 20+ messages in thread
From: Suzuki K Poulose @ 2026-07-06 11:00 UTC (permalink / raw)
To: Fuad Tabba, kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier, Fuad Tabba
On 06/07/2026 09:35, Fuad Tabba wrote:
> Add a guest_memfd file descriptor and offset to kvm__register_mem().
> When guest_memfd >= 0, set KVM_MEM_GUEST_MEMFD and populate the
> guest_memfd fields, binding the memslot to the file. This prepares
> for backing guest RAM with guest_memfd on arm64.
>
> Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
Looks good to me
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> ---
> hw/cfi_flash.c | 2 +-
> include/kvm/kvm.h | 15 +++++++++++++--
> kvm.c | 9 ++++++++-
> 3 files changed, 22 insertions(+), 4 deletions(-)
>
> diff --git a/hw/cfi_flash.c b/hw/cfi_flash.c
> index b68d9e0..4413955 100644
> --- a/hw/cfi_flash.c
> +++ b/hw/cfi_flash.c
> @@ -451,7 +451,7 @@ static int map_flash_memory(struct kvm *kvm, struct cfi_flash_device *sfdev)
> int ret;
>
> ret = kvm__register_mem(kvm, sfdev->base_addr, sfdev->size,
> - sfdev->flash_memory,
> + sfdev->flash_memory, -1, 0,
> KVM_MEM_TYPE_RAM | KVM_MEM_TYPE_READONLY);
> if (!ret)
> sfdev->is_mapped = true;
> diff --git a/include/kvm/kvm.h b/include/kvm/kvm.h
> index eb23e2f..5e7b5eb 100644
> --- a/include/kvm/kvm.h
> +++ b/include/kvm/kvm.h
> @@ -128,11 +128,22 @@ bool kvm__emulate_io(struct kvm_cpu *vcpu, u16 port, void *data, int direction,
> bool kvm__emulate_mmio(struct kvm_cpu *vcpu, u64 phys_addr, u8 *data, u32 len, u8 is_write);
> int kvm__destroy_mem(struct kvm *kvm, u64 guest_phys, u64 size, void *userspace_addr);
> int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size, void *userspace_addr,
> + int guest_memfd, u64 guest_memfd_offset,
> enum kvm_mem_type type);
> static inline int kvm__register_ram(struct kvm *kvm, u64 guest_phys, u64 size,
> void *userspace_addr)
> {
> return kvm__register_mem(kvm, guest_phys, size, userspace_addr,
> + -1, 0, KVM_MEM_TYPE_RAM);
> +}
> +
> +static inline int kvm__register_ram_guest_memfd(struct kvm *kvm, u64 guest_phys,
> + u64 size, void *userspace_addr,
> + int guest_memfd,
> + u64 guest_memfd_offset)
> +{
> + return kvm__register_mem(kvm, guest_phys, size, userspace_addr,
> + guest_memfd, guest_memfd_offset,
> KVM_MEM_TYPE_RAM);
> }
>
> @@ -140,13 +151,13 @@ static inline int kvm__register_dev_mem(struct kvm *kvm, u64 guest_phys,
> u64 size, void *userspace_addr)
> {
> return kvm__register_mem(kvm, guest_phys, size, userspace_addr,
> - KVM_MEM_TYPE_DEVICE);
> + -1, 0, KVM_MEM_TYPE_DEVICE);
> }
>
> static inline int kvm__reserve_mem(struct kvm *kvm, u64 guest_phys, u64 size)
> {
> return kvm__register_mem(kvm, guest_phys, size, NULL,
> - KVM_MEM_TYPE_RESERVED);
> + -1, 0, KVM_MEM_TYPE_RESERVED);
> }
>
> int __must_check kvm__register_iotrap(struct kvm *kvm, u64 phys_addr, u64 len,
> diff --git a/kvm.c b/kvm.c
> index 8a7fd09..3d05756 100644
> --- a/kvm.c
> +++ b/kvm.c
> @@ -237,7 +237,8 @@ out:
> }
>
> int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> - void *userspace_addr, enum kvm_mem_type type)
> + void *userspace_addr, int guest_memfd,
> + u64 guest_memfd_offset, enum kvm_mem_type type)
> {
> struct kvm_mem_bank *merged = NULL;
> struct kvm_mem_bank *bank;
> @@ -327,6 +328,12 @@ int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> .userspace_addr = (unsigned long)userspace_addr,
> };
>
> + if (guest_memfd >= 0) {
> + mem.flags |= KVM_MEM_GUEST_MEMFD;
> + mem.guest_memfd = guest_memfd;
> + mem.guest_memfd_offset = guest_memfd_offset;
> + }
> +
> ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
> if (ret < 0) {
> ret = -errno;
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 4/5] Add guest_memfd support to kvm__register_mem()
2026-07-06 11:00 ` Suzuki K Poulose
@ 2026-07-06 11:13 ` Suzuki K Poulose
2026-07-06 11:25 ` Fuad Tabba
0 siblings, 1 reply; 20+ messages in thread
From: Suzuki K Poulose @ 2026-07-06 11:13 UTC (permalink / raw)
To: Fuad Tabba, kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier, Fuad Tabba
On 06/07/2026 12:00, Suzuki K Poulose wrote:
> On 06/07/2026 09:35, Fuad Tabba wrote:
>> Add a guest_memfd file descriptor and offset to kvm__register_mem().
>> When guest_memfd >= 0, set KVM_MEM_GUEST_MEMFD and populate the
>> guest_memfd fields, binding the memslot to the file. This prepares
>> for backing guest RAM with guest_memfd on arm64.
>>
>> Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
>
> Looks good to me
>
> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Having another look... Thought of adding my comment, no changes to the
above.
>> int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
>> - void *userspace_addr, enum kvm_mem_type type)
>> + void *userspace_addr, int guest_memfd,
>> + u64 guest_memfd_offset, enum kvm_mem_type type)
>> {
>> struct kvm_mem_bank *merged = NULL;
>> struct kvm_mem_bank *bank;
>> @@ -327,6 +328,12 @@ int kvm__register_mem(struct kvm *kvm, u64
>> guest_phys, u64 size,
>> .userspace_addr = (unsigned long)userspace_addr,
>> };
>> + if (guest_memfd >= 0) {
>> + mem.flags |= KVM_MEM_GUEST_MEMFD;
>> + mem.guest_memfd = guest_memfd;
>> + mem.guest_memfd_offset = guest_memfd_offset;
>> + }
It may be worth capturing the fd and the offset in the mem_bank here,
but agree that can be added on the need basis and there are no users
of it for now, without conversion support.
Suzuki
>> +
>> ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
>> if (ret < 0) {
>> ret = -errno;
>
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 5/5] arm64: Add --guest-memfd option to back guest RAM with guest_memfd
2026-07-06 8:35 ` [PATCH kvmtool v1 5/5] arm64: Add --guest-memfd option to back guest RAM with guest_memfd Fuad Tabba
@ 2026-07-06 11:18 ` Suzuki K Poulose
2026-07-06 11:24 ` Fuad Tabba
0 siblings, 1 reply; 20+ messages in thread
From: Suzuki K Poulose @ 2026-07-06 11:18 UTC (permalink / raw)
To: Fuad Tabba, kvm
Cc: kvmarm, Will Deacon, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier, Fuad Tabba
On 06/07/2026 09:35, Fuad Tabba wrote:
> Back guest RAM with a guest_memfd created with GUEST_MEMFD_FLAG_MMAP
> and GUEST_MEMFD_FLAG_INIT_SHARED, mmapped MAP_SHARED to provide the
> userspace mapping.
>
> Protected VMs need in-place shared/private conversion of guest_memfd,
> which the kernel does not support yet, so reject the combination of
> --protected and --guest-memfd.
>
> Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
> ---
> arm64/include/kvm/kvm-config-arch.h | 5 +++-
> arm64/kvm.c | 38 +++++++++++++++++++++++------
> include/kvm/kvm.h | 1 +
> include/kvm/util.h | 1 +
> kvm.c | 1 +
> util/util.c | 34 ++++++++++++++++++++++++++
> 6 files changed, 71 insertions(+), 9 deletions(-)
>
> diff --git a/arm64/include/kvm/kvm-config-arch.h b/arm64/include/kvm/kvm-config-arch.h
> index c2702d5..d8a8ef7 100644
> --- a/arm64/include/kvm/kvm-config-arch.h
> +++ b/arm64/include/kvm/kvm-config-arch.h
> @@ -20,6 +20,7 @@ struct kvm_config_arch {
> bool no_pvtime;
> bool psci;
> bool protected;
> + bool guest_memfd;
> };
>
> int irqchip_parser(const struct option *opt, const char *arg, int unset);
> @@ -73,6 +74,8 @@ int sve_vl_parser(const struct option *opt, const char *arg, int unset);
> OPT_BOOLEAN('\0', "e2h0", &(cfg)->e2h0, \
> "Create guest without VHE support"), \
> OPT_BOOLEAN('\0', "protected", &(cfg)->protected, \
> - "Create a protected VM when pKVM is enabled"),
> + "Create a protected VM when pKVM is enabled"), \
> + OPT_BOOLEAN('\0', "guest-memfd", &(cfg)->guest_memfd, \
> + "Use guest_memfd to back guest RAM"),
>
> #endif /* ARM_COMMON__KVM_CONFIG_ARCH_H */
> diff --git a/arm64/kvm.c b/arm64/kvm.c
> index fb0b98d..b594578 100644
> --- a/arm64/kvm.c
> +++ b/arm64/kvm.c
> @@ -49,9 +49,14 @@ void kvm__init_ram(struct kvm *kvm)
> kvm->arch.ram_alloc_size = kvm->ram_size;
> if (!kvm->cfg.hugetlbfs_path)
> kvm->arch.ram_alloc_size += SZ_2M;
> - kvm->arch.ram_alloc_start = mmap_anon_or_hugetlbfs(kvm,
> - kvm->cfg.hugetlbfs_path,
> - kvm->arch.ram_alloc_size);
> +
> + if (kvm->cfg.arch.guest_memfd)
> + kvm->arch.ram_alloc_start =
> + mmap_guest_memfd(kvm, kvm->arch.ram_alloc_size);
> + else
> + kvm->arch.ram_alloc_start =
> + mmap_anon_or_hugetlbfs(kvm, kvm->cfg.hugetlbfs_path,
> + kvm->arch.ram_alloc_size);
>
> if (kvm->arch.ram_alloc_start == MAP_FAILED)
> die("Failed to map %lld bytes for guest memory (%d)",
> @@ -60,17 +65,25 @@ void kvm__init_ram(struct kvm *kvm)
> kvm->ram_start = (void *)ALIGN((unsigned long)kvm->arch.ram_alloc_start,
> SZ_2M);
>
> - madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
> - MADV_MERGEABLE);
> + if (!kvm->cfg.arch.guest_memfd) {
> + madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
> + MADV_MERGEABLE);
>
> - madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
> - MADV_HUGEPAGE);
> + madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
> + MADV_HUGEPAGE);
> + }
>
> phys_start = kvm->cfg.ram_addr;
> phys_size = kvm->ram_size;
> host_mem = kvm->ram_start;
>
> - err = kvm__register_ram(kvm, phys_start, phys_size, host_mem);
> + if (kvm->cfg.arch.guest_memfd)
> + err = kvm__register_ram_guest_memfd(kvm, phys_start,
> + phys_size, host_mem,
> + kvm->ram_guest_memfd,
> + host_mem - kvm->arch.ram_alloc_start);
> + else
> + err = kvm__register_ram(kvm, phys_start, phys_size, host_mem);
> if (err)
> die("Failed to register %lld bytes of memory at physical "
> "address 0x%llx [err %d]", phys_size, phys_start, err);
> @@ -468,6 +481,15 @@ void kvm__arch_validate_cfg(struct kvm *kvm)
> if (kvm->cfg.arch.e2h0 && !kvm->cfg.arch.nested_virt)
> pr_warning("--e2h0 requires --nested, ignoring");
>
> + if (kvm->cfg.arch.guest_memfd) {
> + /* Requires in-place shared/private conversion of guest_memfd. */
> + if (kvm->cfg.arch.protected)
> + die("Protected VMs do not yet support guest_memfd");
> +
> + if (kvm->cfg.hugetlbfs_path)
> + die("guest_memfd cannot be backed by hugetlbfs");
> + }
> +
> if (kvm->cfg.arch.protected) {
> if (kvm->cfg.ram_size &&
> kvm->cfg.ram_size < DMA_MEM_REGION_SIZE) {
> diff --git a/include/kvm/kvm.h b/include/kvm/kvm.h
> index 5e7b5eb..95dc52f 100644
> --- a/include/kvm/kvm.h
> +++ b/include/kvm/kvm.h
> @@ -90,6 +90,7 @@ struct kvm {
> u64 ram_size; /* Guest memory size, in bytes */
> void *ram_start;
> u64 ram_pagesize;
> + int ram_guest_memfd; /* guest_memfd backing RAM, -1 if none */
> struct mutex mem_banks_lock;
> struct list_head mem_banks;
>
> diff --git a/include/kvm/util.h b/include/kvm/util.h
> index a6dba11..9e23431 100644
> --- a/include/kvm/util.h
> +++ b/include/kvm/util.h
> @@ -148,5 +148,6 @@ static inline int pow2_size(unsigned long x)
> struct kvm;
> void *mmap_hugetlbfs(struct kvm *kvm, const char *htlbfs_path, u64 size);
> void *mmap_anon_or_hugetlbfs(struct kvm *kvm, const char *hugetlbfs_path, u64 size);
> +void *mmap_guest_memfd(struct kvm *kvm, u64 size);
>
> #endif /* KVM__UTIL_H */
> diff --git a/kvm.c b/kvm.c
> index 3d05756..a400dd2 100644
> --- a/kvm.c
> +++ b/kvm.c
> @@ -162,6 +162,7 @@ struct kvm *kvm__new(void)
> mutex_init(&kvm->mem_banks_lock);
> kvm->sys_fd = -1;
> kvm->vm_fd = -1;
> + kvm->ram_guest_memfd = -1;
>
> #ifdef KVM_BRLOCK_DEBUG
> kvm->brlock_sem = (pthread_rwlock_t) PTHREAD_RWLOCK_INITIALIZER;
> diff --git a/util/util.c b/util/util.c
> index 05d49c4..c60fe02 100644
> --- a/util/util.c
> +++ b/util/util.c
> @@ -6,7 +6,9 @@
> #include "kvm/util.h"
>
> #include <kvm/kvm.h>
> +#include <linux/kvm.h>
> #include <linux/magic.h> /* For HUGETLBFS_MAGIC */
> +#include <sys/ioctl.h>
> #include <sys/mman.h>
> #include <sys/stat.h>
> #include <sys/statfs.h>
> @@ -169,3 +171,35 @@ void *mmap_anon_or_hugetlbfs(struct kvm *kvm, const char *hugetlbfs_path, u64 si
> return mmap(NULL, size, PROT_RW, MAP_ANON_NORESERVE, -1, 0);
> }
> }
> +
> +/*
> + * Map guest RAM backed by a guest_memfd. The file descriptor must stay open
> + * for the lifetime of the VM: closing it invalidates the memslot bindings.
> + */
> +void *mmap_guest_memfd(struct kvm *kvm, u64 size)
> +{
> + u64 flags = GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_INIT_SHARED;
> + struct kvm_create_guest_memfd gmem = {
> + .size = size,
> + .flags = flags,
> + };
> + void *addr;
> + int ret;
> +
> + ret = ioctl(kvm->vm_fd, KVM_CHECK_EXTENSION, KVM_CAP_GUEST_MEMFD_FLAGS);
> + if (ret < 0 || ((u64)ret & flags) != flags)
> + die("kernel does not support guest_memfd with mmap");
Minor nit: "VM type doesn't support..", to be precise ?
Suzuki
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 5/5] arm64: Add --guest-memfd option to back guest RAM with guest_memfd
2026-07-06 11:18 ` Suzuki K Poulose
@ 2026-07-06 11:24 ` Fuad Tabba
0 siblings, 0 replies; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 11:24 UTC (permalink / raw)
To: Suzuki K Poulose
Cc: kvm, kvmarm, Will Deacon, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier
On Mon, 6 Jul 2026 at 12:19, Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>
> On 06/07/2026 09:35, Fuad Tabba wrote:
> > Back guest RAM with a guest_memfd created with GUEST_MEMFD_FLAG_MMAP
> > and GUEST_MEMFD_FLAG_INIT_SHARED, mmapped MAP_SHARED to provide the
> > userspace mapping.
> >
> > Protected VMs need in-place shared/private conversion of guest_memfd,
> > which the kernel does not support yet, so reject the combination of
> > --protected and --guest-memfd.
> >
> > Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
> > ---
> > arm64/include/kvm/kvm-config-arch.h | 5 +++-
> > arm64/kvm.c | 38 +++++++++++++++++++++++------
> > include/kvm/kvm.h | 1 +
> > include/kvm/util.h | 1 +
> > kvm.c | 1 +
> > util/util.c | 34 ++++++++++++++++++++++++++
> > 6 files changed, 71 insertions(+), 9 deletions(-)
> >
> > diff --git a/arm64/include/kvm/kvm-config-arch.h b/arm64/include/kvm/kvm-config-arch.h
> > index c2702d5..d8a8ef7 100644
> > --- a/arm64/include/kvm/kvm-config-arch.h
> > +++ b/arm64/include/kvm/kvm-config-arch.h
> > @@ -20,6 +20,7 @@ struct kvm_config_arch {
> > bool no_pvtime;
> > bool psci;
> > bool protected;
> > + bool guest_memfd;
> > };
> >
> > int irqchip_parser(const struct option *opt, const char *arg, int unset);
> > @@ -73,6 +74,8 @@ int sve_vl_parser(const struct option *opt, const char *arg, int unset);
> > OPT_BOOLEAN('\0', "e2h0", &(cfg)->e2h0, \
> > "Create guest without VHE support"), \
> > OPT_BOOLEAN('\0', "protected", &(cfg)->protected, \
> > - "Create a protected VM when pKVM is enabled"),
> > + "Create a protected VM when pKVM is enabled"), \
> > + OPT_BOOLEAN('\0', "guest-memfd", &(cfg)->guest_memfd, \
> > + "Use guest_memfd to back guest RAM"),
> >
> > #endif /* ARM_COMMON__KVM_CONFIG_ARCH_H */
> > diff --git a/arm64/kvm.c b/arm64/kvm.c
> > index fb0b98d..b594578 100644
> > --- a/arm64/kvm.c
> > +++ b/arm64/kvm.c
> > @@ -49,9 +49,14 @@ void kvm__init_ram(struct kvm *kvm)
> > kvm->arch.ram_alloc_size = kvm->ram_size;
> > if (!kvm->cfg.hugetlbfs_path)
> > kvm->arch.ram_alloc_size += SZ_2M;
> > - kvm->arch.ram_alloc_start = mmap_anon_or_hugetlbfs(kvm,
> > - kvm->cfg.hugetlbfs_path,
> > - kvm->arch.ram_alloc_size);
> > +
> > + if (kvm->cfg.arch.guest_memfd)
> > + kvm->arch.ram_alloc_start =
> > + mmap_guest_memfd(kvm, kvm->arch.ram_alloc_size);
> > + else
> > + kvm->arch.ram_alloc_start =
> > + mmap_anon_or_hugetlbfs(kvm, kvm->cfg.hugetlbfs_path,
> > + kvm->arch.ram_alloc_size);
> >
> > if (kvm->arch.ram_alloc_start == MAP_FAILED)
> > die("Failed to map %lld bytes for guest memory (%d)",
> > @@ -60,17 +65,25 @@ void kvm__init_ram(struct kvm *kvm)
> > kvm->ram_start = (void *)ALIGN((unsigned long)kvm->arch.ram_alloc_start,
> > SZ_2M);
> >
> > - madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
> > - MADV_MERGEABLE);
> > + if (!kvm->cfg.arch.guest_memfd) {
> > + madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
> > + MADV_MERGEABLE);
> >
> > - madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
> > - MADV_HUGEPAGE);
> > + madvise(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size,
> > + MADV_HUGEPAGE);
> > + }
> >
> > phys_start = kvm->cfg.ram_addr;
> > phys_size = kvm->ram_size;
> > host_mem = kvm->ram_start;
> >
> > - err = kvm__register_ram(kvm, phys_start, phys_size, host_mem);
> > + if (kvm->cfg.arch.guest_memfd)
> > + err = kvm__register_ram_guest_memfd(kvm, phys_start,
> > + phys_size, host_mem,
> > + kvm->ram_guest_memfd,
> > + host_mem - kvm->arch.ram_alloc_start);
> > + else
> > + err = kvm__register_ram(kvm, phys_start, phys_size, host_mem);
> > if (err)
> > die("Failed to register %lld bytes of memory at physical "
> > "address 0x%llx [err %d]", phys_size, phys_start, err);
> > @@ -468,6 +481,15 @@ void kvm__arch_validate_cfg(struct kvm *kvm)
> > if (kvm->cfg.arch.e2h0 && !kvm->cfg.arch.nested_virt)
> > pr_warning("--e2h0 requires --nested, ignoring");
> >
> > + if (kvm->cfg.arch.guest_memfd) {
> > + /* Requires in-place shared/private conversion of guest_memfd. */
> > + if (kvm->cfg.arch.protected)
> > + die("Protected VMs do not yet support guest_memfd");
> > +
> > + if (kvm->cfg.hugetlbfs_path)
> > + die("guest_memfd cannot be backed by hugetlbfs");
> > + }
> > +
> > if (kvm->cfg.arch.protected) {
> > if (kvm->cfg.ram_size &&
> > kvm->cfg.ram_size < DMA_MEM_REGION_SIZE) {
> > diff --git a/include/kvm/kvm.h b/include/kvm/kvm.h
> > index 5e7b5eb..95dc52f 100644
> > --- a/include/kvm/kvm.h
> > +++ b/include/kvm/kvm.h
> > @@ -90,6 +90,7 @@ struct kvm {
> > u64 ram_size; /* Guest memory size, in bytes */
> > void *ram_start;
> > u64 ram_pagesize;
> > + int ram_guest_memfd; /* guest_memfd backing RAM, -1 if none */
> > struct mutex mem_banks_lock;
> > struct list_head mem_banks;
> >
> > diff --git a/include/kvm/util.h b/include/kvm/util.h
> > index a6dba11..9e23431 100644
> > --- a/include/kvm/util.h
> > +++ b/include/kvm/util.h
> > @@ -148,5 +148,6 @@ static inline int pow2_size(unsigned long x)
> > struct kvm;
> > void *mmap_hugetlbfs(struct kvm *kvm, const char *htlbfs_path, u64 size);
> > void *mmap_anon_or_hugetlbfs(struct kvm *kvm, const char *hugetlbfs_path, u64 size);
> > +void *mmap_guest_memfd(struct kvm *kvm, u64 size);
> >
> > #endif /* KVM__UTIL_H */
> > diff --git a/kvm.c b/kvm.c
> > index 3d05756..a400dd2 100644
> > --- a/kvm.c
> > +++ b/kvm.c
> > @@ -162,6 +162,7 @@ struct kvm *kvm__new(void)
> > mutex_init(&kvm->mem_banks_lock);
> > kvm->sys_fd = -1;
> > kvm->vm_fd = -1;
> > + kvm->ram_guest_memfd = -1;
> >
> > #ifdef KVM_BRLOCK_DEBUG
> > kvm->brlock_sem = (pthread_rwlock_t) PTHREAD_RWLOCK_INITIALIZER;
> > diff --git a/util/util.c b/util/util.c
> > index 05d49c4..c60fe02 100644
> > --- a/util/util.c
> > +++ b/util/util.c
> > @@ -6,7 +6,9 @@
> > #include "kvm/util.h"
> >
> > #include <kvm/kvm.h>
> > +#include <linux/kvm.h>
> > #include <linux/magic.h> /* For HUGETLBFS_MAGIC */
> > +#include <sys/ioctl.h>
> > #include <sys/mman.h>
> > #include <sys/stat.h>
> > #include <sys/statfs.h>
> > @@ -169,3 +171,35 @@ void *mmap_anon_or_hugetlbfs(struct kvm *kvm, const char *hugetlbfs_path, u64 si
> > return mmap(NULL, size, PROT_RW, MAP_ANON_NORESERVE, -1, 0);
> > }
> > }
> > +
> > +/*
> > + * Map guest RAM backed by a guest_memfd. The file descriptor must stay open
> > + * for the lifetime of the VM: closing it invalidates the memslot bindings.
> > + */
> > +void *mmap_guest_memfd(struct kvm *kvm, u64 size)
> > +{
> > + u64 flags = GUEST_MEMFD_FLAG_MMAP | GUEST_MEMFD_FLAG_INIT_SHARED;
> > + struct kvm_create_guest_memfd gmem = {
> > + .size = size,
> > + .flags = flags,
> > + };
> > + void *addr;
> > + int ret;
> > +
> > + ret = ioctl(kvm->vm_fd, KVM_CHECK_EXTENSION, KVM_CAP_GUEST_MEMFD_FLAGS);
> > + if (ret < 0 || ((u64)ret & flags) != flags)
> > + die("kernel does not support guest_memfd with mmap");
>
> Minor nit: "VM type doesn't support..", to be precise ?
Done,
/fuad
>
> Suzuki
> IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 4/5] Add guest_memfd support to kvm__register_mem()
2026-07-06 11:13 ` Suzuki K Poulose
@ 2026-07-06 11:25 ` Fuad Tabba
2026-07-06 12:34 ` Suzuki K Poulose
0 siblings, 1 reply; 20+ messages in thread
From: Fuad Tabba @ 2026-07-06 11:25 UTC (permalink / raw)
To: Suzuki K Poulose
Cc: kvm, kvmarm, Will Deacon, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier
On Mon, 6 Jul 2026 at 12:14, Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>
>
>
> On 06/07/2026 12:00, Suzuki K Poulose wrote:
> > On 06/07/2026 09:35, Fuad Tabba wrote:
> >> Add a guest_memfd file descriptor and offset to kvm__register_mem().
> >> When guest_memfd >= 0, set KVM_MEM_GUEST_MEMFD and populate the
> >> guest_memfd fields, binding the memslot to the file. This prepares
> >> for backing guest RAM with guest_memfd on arm64.
> >>
> >> Signed-off-by: Fuad Tabba <fuad.tabba@linux.dev>
> >
> > Looks good to me
> >
> > Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
>
> Having another look... Thought of adding my comment, no changes to the
> above.
Thanks!
>
>
> >> int kvm__register_mem(struct kvm *kvm, u64 guest_phys, u64 size,
> >> - void *userspace_addr, enum kvm_mem_type type)
> >> + void *userspace_addr, int guest_memfd,
> >> + u64 guest_memfd_offset, enum kvm_mem_type type)
> >> {
> >> struct kvm_mem_bank *merged = NULL;
> >> struct kvm_mem_bank *bank;
> >> @@ -327,6 +328,12 @@ int kvm__register_mem(struct kvm *kvm, u64
> >> guest_phys, u64 size,
> >> .userspace_addr = (unsigned long)userspace_addr,
> >> };
> >> + if (guest_memfd >= 0) {
> >> + mem.flags |= KVM_MEM_GUEST_MEMFD;
> >> + mem.guest_memfd = guest_memfd;
> >> + mem.guest_memfd_offset = guest_memfd_offset;
> >> + }
>
> It may be worth capturing the fd and the offset in the mem_bank here,
> but agree that can be added on the need basis and there are no users
> of it for now, without conversion support.
Didn't want to over-engineer this. We'll add it when we need it, which
is soon I hope :)
Cheers,
/fuad
>
> Suzuki
>
>
> >> +
> >> ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
> >> if (ret < 0) {
> >> ret = -errno;
> >
>
> IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH kvmtool v1 4/5] Add guest_memfd support to kvm__register_mem()
2026-07-06 11:25 ` Fuad Tabba
@ 2026-07-06 12:34 ` Suzuki K Poulose
0 siblings, 0 replies; 20+ messages in thread
From: Suzuki K Poulose @ 2026-07-06 12:34 UTC (permalink / raw)
To: Fuad Tabba
Cc: kvm, kvmarm, Will Deacon, Alexandru Elisei, Andre Przywara,
Oliver Upton, Marc Zyngier
On 06/07/2026 12:25, Fuad Tabba wrote:
> On Mon, 6 Jul 2026 at 12:14, Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>>
>>>> +
>>>> ret = ioctl(kvm->vm_fd, KVM_SET_USER_MEMORY_REGION2, &mem);
>>>> if (ret < 0) {
>>>> ret = -errno;
>>>
>>
>> IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
Sorry about this^, hopefully I have fixed this now :-(
Suzuki
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-07-06 12:36 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 8:35 [PATCH kvmtool v1 0/5] Add guest_memfd support for arm64 Fuad Tabba
2026-07-06 8:35 ` [PATCH kvmtool v1 1/5] Initialize the return value in kvm__for_each_mem_bank() Fuad Tabba
2026-07-06 8:35 ` [PATCH kvmtool v1 2/5] Remove newline from end of die() aborts Fuad Tabba
2026-07-06 10:53 ` Suzuki K Poulose
2026-07-06 10:57 ` Fuad Tabba
2026-07-06 8:35 ` [PATCH kvmtool v1 3/5] Use kvm_userspace_memory_region2 for all memory registration Fuad Tabba
2026-07-06 10:23 ` Suzuki K Poulose
2026-07-06 10:41 ` Fuad Tabba
2026-07-06 10:46 ` Will Deacon
2026-07-06 10:47 ` Fuad Tabba
2026-07-06 10:57 ` Alexandru Elisei
2026-07-06 10:59 ` Fuad Tabba
2026-07-06 8:35 ` [PATCH kvmtool v1 4/5] Add guest_memfd support to kvm__register_mem() Fuad Tabba
2026-07-06 11:00 ` Suzuki K Poulose
2026-07-06 11:13 ` Suzuki K Poulose
2026-07-06 11:25 ` Fuad Tabba
2026-07-06 12:34 ` Suzuki K Poulose
2026-07-06 8:35 ` [PATCH kvmtool v1 5/5] arm64: Add --guest-memfd option to back guest RAM with guest_memfd Fuad Tabba
2026-07-06 11:18 ` Suzuki K Poulose
2026-07-06 11:24 ` Fuad Tabba
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox