The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: Sriram Nambakam <snambakam@linux.microsoft.com>
To: kvm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Subject: [RFC PATCH v2 6/8] vm_planes: add hypervisor-assisted plane bootstrap
Date: Mon, 10 Aug 2026 18:52:41 -0700	[thread overview]
Message-ID: <20260811015243.188486-7-snambakam@linux.microsoft.com> (raw)
In-Reply-To: <20260811015243.188486-1-snambakam@linux.microsoft.com>

Add the guest-side VM-planes bootstrap under CONFIG_VM_PLANES.  It reads
the per-plane configuration (config-vm-planes) and the plane kernel images
from the initramfs, allocates plane memory and creates the planes via the
KVM_HC_VM_PLANES_CONFIG / KVM_HC_VM_PLANES_ACTIVATE paravirt hypercalls,
and loads RAW or ELF plane kernels into plane memory.

The orchestration is exposed as vm_planes_bootstrap() so a consumer can
drive it at the right point in boot; the x86 hypercall wrappers
alloc_vm_planes() / activate_vm_planes() live in cpu/common.c.

Only software planes are supported; there is no memory protection or
sealing here.
---
 arch/x86/kernel/cpu/common.c |  64 ++++
 include/linux/vm_planes.h    |  46 +++
 init/Kconfig                 |  18 +
 init/Makefile                |   4 +
 init/vm_planes.c             | 636 +++++++++++++++++++++++++++++++++++
 5 files changed, 768 insertions(+)
 create mode 100644 include/linux/vm_planes.h
 create mode 100644 init/vm_planes.c

diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index a3df21d26460..2489af105c18 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -28,8 +28,11 @@
 #include <linux/stackprotector.h>
 #include <linux/utsname.h>
 #include <linux/efi.h>
+#include <linux/kvm_para.h>
+#include <linux/vm_planes.h>
 
 #include <asm/alternative.h>
+#include <asm/kvm_para.h>
 #include <asm/cmdline.h>
 #include <asm/cpuid/api.h>
 #include <asm/perf_event.h>
@@ -2664,3 +2667,64 @@ void __init arch_cpu_finalize_init(void)
 	 */
 	mem_encrypt_init();
 }
+
+#ifdef CONFIG_VM_PLANES
+int __init alloc_vm_planes(unsigned int plane_count,
+			   struct vm_plane_config *plane_cfg)
+{
+	phys_addr_t phys;
+	long ret;
+
+	if (!plane_count || !plane_cfg)
+		return -EINVAL;
+
+	if (!kvm_para_available()) {
+		pr_warn("vm_planes: hypercall interface unavailable\n");
+		return -ENODEV;
+	}
+
+	phys = virt_to_phys((void *)plane_cfg);
+
+	if (sizeof(unsigned long) < sizeof(phys_addr_t) && phys > ULONG_MAX) {
+		pr_warn("vm_planes: shared config address exceeds hypercall register width\n");
+		return -EOVERFLOW;
+	}
+
+	ret = kvm_hypercall2(KVM_HC_VM_PLANES_CONFIG,
+			     (unsigned long)phys,
+			     plane_count);
+	if (ret < 0) {
+		pr_warn("vm_planes: hypercall failed: %ld\n", ret);
+		return (int)ret;
+	}
+
+	return 0;
+}
+
+int __init activate_vm_planes(unsigned int plane_count,
+			      struct vm_plane_config *plane_cfg)
+{
+	phys_addr_t phys;
+	long ret;
+
+	if (!plane_count || !plane_cfg)
+		return -EINVAL;
+
+	if (!kvm_para_available()) {
+		pr_warn("vm_planes: hypercall interface unavailable\n");
+		return -ENODEV;
+	}
+
+	phys = virt_to_phys((void *)plane_cfg);
+
+	ret = kvm_hypercall2(KVM_HC_VM_PLANES_ACTIVATE,
+			     (unsigned long)phys,
+			     plane_count);
+	if (ret < 0) {
+		pr_warn("vm_planes: activate hypercall failed: %ld\n", ret);
+		return (int)ret;
+	}
+
+	return 0;
+}
+#endif /* CONFIG_VM_PLANES */
diff --git a/include/linux/vm_planes.h b/include/linux/vm_planes.h
new file mode 100644
index 000000000000..e76cbfd99c6d
--- /dev/null
+++ b/include/linux/vm_planes.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_VM_PLANES_H
+#define _LINUX_VM_PLANES_H
+
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+
+#ifdef CONFIG_VM_PLANES
+
+#define VM_PLANE_KERNEL_NAME_MAX	128
+#define VM_PLANE_CMDLINE_MAX		512
+
+enum vm_plane_kernel_format {
+	VM_PLANE_KFMT_RAW = 0,
+	VM_PLANE_KFMT_BZIMAGE,
+	VM_PLANE_KFMT_ELF,
+};
+
+struct vm_plane_config {
+	phys_addr_t load_offset;
+	phys_addr_t memory_size;
+	phys_addr_t entry_point;
+	unsigned int kernel_format;
+	char kernel[VM_PLANE_KERNEL_NAME_MAX];
+	char cmdline[VM_PLANE_CMDLINE_MAX];
+};
+
+int __init load_vm_plane_kernels(unsigned int plane_count,
+				 struct vm_plane_config *plane_cfg);
+
+int __init alloc_vm_planes(unsigned int plane_count,
+			   struct vm_plane_config *plane_cfg);
+
+int __init activate_vm_planes(unsigned int plane_count,
+			      struct vm_plane_config *plane_cfg);
+
+int __init vm_planes_bootstrap(void);
+
+#else /* !CONFIG_VM_PLANES */
+
+static inline int vm_planes_bootstrap(void) { return -ENODEV; }
+
+#endif /* CONFIG_VM_PLANES */
+
+#endif /* _LINUX_VM_PLANES_H */
diff --git a/init/Kconfig b/init/Kconfig
index 10f2013b5321..bdb692f38a90 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -2259,6 +2259,24 @@ source "kernel/Kconfig.kexec"
 
 source "kernel/liveupdate/Kconfig"
 
+config VM_PLANES
+	bool "Enable VM planes early boot support" if EXPERT
+	depends on KVM_GUEST
+	default n
+	help
+	  Enable hypervisor-assisted multi-kernel (VM planes) support.
+
+	  When enabled, and when the VBS backend requests it, the kernel sets
+	  up the configured planes from the VBS enable path (late_initcall,
+	  after device drivers and before userspace).  The plane configuration
+	  and per-plane kernel images are read from the initramfs.
+
+	  The initrd config-vm-planes file is expected to provide per-plane
+	  entries for PLANE_<id>_KERNEL, PLANE_<id>_LOAD_OFFSET, and
+	  PLANE_<id>_MEMORY_SIZE.
+
+	  If unsure, say N.
+
 endmenu		# General setup
 
 source "arch/Kconfig"
diff --git a/init/Makefile b/init/Makefile
index d6f75d8907e0..100b3fcda37a 100644
--- a/init/Makefile
+++ b/init/Makefile
@@ -14,6 +14,10 @@ endif
 obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o
 obj-$(CONFIG_INITRAMFS_TEST)   += initramfs_test.o
 
+# vm_planes.o must link AFTER initramfs.o so that it reads the plane config
+# and kernels from the populated rootfs.
+obj-$(CONFIG_VM_PLANES)                += vm_planes.o
+
 obj-y                          += init_task.o
 
 mounts-y			:= do_mounts.o
diff --git a/init/vm_planes.c b/init/vm_planes.c
new file mode 100644
index 000000000000..27994e4a915a
--- /dev/null
+++ b/init/vm_planes.c
@@ -0,0 +1,636 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/init.h>
+#include <linux/initrd.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/kstrtox.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/kvm_para.h>
+#include <linux/vm_planes.h>
+#include <linux/elf.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <asm/cpu.h>
+#include <asm/kvm_para.h>
+
+#ifdef CONFIG_VM_PLANES
+
+#define VM_PLANES_CONFIG_FILE		"config-vm-planes"
+#define VM_PLANES_DEFAULT_COUNT		1
+
+struct vm_plane_parse_state {
+	phys_addr_t load_offset;
+	phys_addr_t memory_size;
+	unsigned int kernel_format;
+	char kernel[VM_PLANE_KERNEL_NAME_MAX];
+	char cmdline[VM_PLANE_CMDLINE_MAX];
+};
+
+#define VM_PLANES_UNSET_VALUE	((phys_addr_t)~0)
+
+/*
+ * Read a file from the rootfs into a newly allocated buffer.
+ * Caller must kfree(*out_data) when done.
+ */
+static int __init vm_planes_read_file(const char *path,
+				      void **out_data, loff_t *out_size)
+{
+	struct file *fp;
+	loff_t fsize;
+	void *buf;
+	ssize_t rd;
+
+	fp = filp_open(path, O_RDONLY, 0);
+	if (IS_ERR(fp))
+		return PTR_ERR(fp);
+
+	fsize = i_size_read(file_inode(fp));
+	if (fsize <= 0) {
+		fput(fp);
+		return -ENODATA;
+	}
+
+	buf = kvmalloc(fsize, GFP_KERNEL);
+	if (!buf) {
+		fput(fp);
+		return -ENOMEM;
+	}
+
+	rd = kernel_read(fp, buf, fsize, &(loff_t){0});
+	fput(fp);
+
+	if (rd != fsize) {
+		kvfree(buf);
+		return (rd < 0) ? (int)rd : -EIO;
+	}
+
+	*out_data = buf;
+	*out_size = fsize;
+	return 0;
+}
+
+/* ---- Config file parser (unchanged) ---- */
+
+static int __init parse_plane_count_line(const char *line, size_t len,
+					 unsigned int *plane_count)
+{
+	const char *keys[] = { "PLANE_COUNT=", "CONFIG_PLANE_COUNT=" };
+	unsigned int i;
+
+	while (len && (*line == ' ' || *line == '\t')) {
+		line++;
+		len--;
+	}
+
+	if (!len || *line == '#')
+		return -ENOENT;
+
+	for (i = 0; i < ARRAY_SIZE(keys); i++) {
+		size_t key_len = strlen(keys[i]);
+		size_t val_len = 0;
+		char tmp[32];
+
+		if (len <= key_len || strncmp(line, keys[i], key_len))
+			continue;
+
+		line += key_len;
+		len -= key_len;
+		while (val_len < len && line[val_len] != ' ' &&
+		       line[val_len] != '\t' && line[val_len] != '#')
+			val_len++;
+
+		if (!val_len || val_len >= sizeof(tmp))
+			return -EINVAL;
+
+		memcpy(tmp, line, val_len);
+		tmp[val_len] = '\0';
+
+		if (kstrtouint(tmp, 0, plane_count))
+			return -EINVAL;
+		if (!*plane_count)
+			return -EINVAL;
+
+		return 0;
+	}
+
+	return -ENOENT;
+}
+
+static int __init parse_plane_count_kconfig(const char *buf, size_t len,
+					    unsigned int *plane_count)
+{
+	const char *p = buf;
+	const char *end = buf + len;
+
+	while (p < end) {
+		const char *eol = memchr(p, '\n', end - p);
+		size_t line_len = eol ? (size_t)(eol - p) : (size_t)(end - p);
+		int ret = parse_plane_count_line(p, line_len, plane_count);
+
+		if (!ret)
+			return 0;
+
+		p += line_len;
+		if (p < end && *p == '\n')
+			p++;
+	}
+
+	return -ENOENT;
+}
+
+static int __init parse_plane_cfg_line(const char *line, size_t len,
+				       unsigned int plane_count,
+				       struct vm_plane_config *plane_cfg,
+				       struct vm_plane_parse_state *state)
+{
+	char tmp[VM_PLANE_CMDLINE_MAX + 64];
+	char *p, *key, *val;
+	unsigned int plane_id;
+	u64 parsed_u64;
+	phys_addr_t parsed;
+
+	if (len >= sizeof(tmp))
+		return -E2BIG;
+
+	memcpy(tmp, line, len);
+	tmp[len] = '\0';
+
+	p = strim(tmp);
+	if (!*p || *p == '#')
+		return -ENOENT;
+
+	val = strchr(p, '#');
+	if (val)
+		*val = '\0';
+	p = strim(p);
+	if (!*p)
+		return -ENOENT;
+
+	if (!strncmp(p, "CONFIG_", 7))
+		p += 7;
+
+	if (strncmp(p, "PLANE_", 6))
+		return -ENOENT;
+	p += 6;
+
+	key = strchr(p, '_');
+	if (!key)
+		return -ENOENT;
+	*key++ = '\0';
+
+	if (kstrtouint(p, 10, &plane_id) || plane_id >= plane_count)
+		return -EINVAL;
+
+	val = strchr(key, '=');
+	if (!val)
+		return -EINVAL;
+	*val++ = '\0';
+
+	key = strim(key);
+	val = strim(val);
+	if (!*val)
+		return -EINVAL;
+
+	if (!strcmp(key, "KERNEL")) {
+		size_t val_len = strlen(val);
+
+		if (val[0] == '"') {
+			if (val_len < 2 || val[val_len - 1] != '"')
+				return -EINVAL;
+			val[val_len - 1] = '\0';
+			val++;
+			val = strim(val);
+		}
+
+		if (!*val)
+			return -EINVAL;
+
+		if (strscpy(plane_cfg[plane_id].kernel, val,
+			    sizeof(plane_cfg[plane_id].kernel)) < 0)
+			return -EINVAL;
+
+		strscpy(state[plane_id].kernel, val,
+			sizeof(state[plane_id].kernel));
+		return 0;
+	}
+
+	if (!strcmp(key, "KERNEL_FORMAT")) {
+		unsigned int fmt;
+
+		if (!strcasecmp(val, "raw"))
+			fmt = VM_PLANE_KFMT_RAW;
+		else if (!strcasecmp(val, "bzimage"))
+			fmt = VM_PLANE_KFMT_BZIMAGE;
+		else if (!strcasecmp(val, "elf"))
+			fmt = VM_PLANE_KFMT_ELF;
+		else
+			return -EINVAL;
+
+		plane_cfg[plane_id].kernel_format = fmt;
+		state[plane_id].kernel_format = fmt;
+		return 0;
+	}
+
+	if (!strcmp(key, "CMDLINE")) {
+		size_t val_len = strlen(val);
+
+		if (val_len >= 2 && val[0] == '"') {
+			if (val[val_len - 1] != '"')
+				return -EINVAL;
+			val[val_len - 1] = '\0';
+			val++;
+		}
+
+		if (strscpy(plane_cfg[plane_id].cmdline, val,
+			    sizeof(plane_cfg[plane_id].cmdline)) < 0)
+			return -E2BIG;
+
+		strscpy(state[plane_id].cmdline, val,
+			sizeof(state[plane_id].cmdline));
+		return 0;
+	}
+
+	if (kstrtou64(val, 0, &parsed_u64))
+		return -EINVAL;
+
+	if (parsed_u64 > (u64)VM_PLANES_UNSET_VALUE)
+		return -ERANGE;
+
+	parsed = (phys_addr_t)parsed_u64;
+
+	if (!strcmp(key, "LOAD_OFFSET")) {
+		plane_cfg[plane_id].load_offset = parsed;
+		state[plane_id].load_offset = parsed;
+		return 0;
+	}
+
+	if (!strcmp(key, "MEMORY_SIZE")) {
+		plane_cfg[plane_id].memory_size = parsed;
+		state[plane_id].memory_size = parsed;
+		return 0;
+	}
+
+	return -ENOENT;
+}
+
+static int __init parse_vm_planes_kconfig(const char *buf, size_t len,
+				  unsigned int *plane_count,
+				  struct vm_plane_config **plane_cfg)
+{
+	const char *p = buf;
+	const char *end = buf + len;
+	struct vm_plane_parse_state *state;
+	unsigned int i;
+	int ret;
+
+	ret = parse_plane_count_kconfig(buf, len, plane_count);
+	if (ret)
+		return ret;
+
+	if (*plane_count > UINT_MAX / sizeof(**plane_cfg))
+		return -E2BIG;
+
+	*plane_cfg = kzalloc(*plane_count * sizeof(**plane_cfg), GFP_KERNEL);
+	if (!*plane_cfg)
+		return -ENOMEM;
+
+	state = kzalloc(*plane_count * sizeof(*state), GFP_KERNEL);
+	if (!state)
+		return -ENOMEM;
+
+	for (i = 0; i < *plane_count; i++) {
+		state[i].load_offset = VM_PLANES_UNSET_VALUE;
+		state[i].memory_size = VM_PLANES_UNSET_VALUE;
+		state[i].kernel[0] = '\0';
+		state[i].cmdline[0] = '\0';
+	}
+
+	while (p < end) {
+		const char *eol = memchr(p, '\n', end - p);
+		size_t line_len = eol ? (size_t)(eol - p) : (size_t)(end - p);
+
+		ret = parse_plane_cfg_line(p, line_len, *plane_count,
+					   *plane_cfg, state);
+		if (ret && ret != -ENOENT)
+			return ret;
+
+		p += line_len;
+		if (p < end && *p == '\n')
+			p++;
+	}
+
+	for (i = 1; i < *plane_count; i++) {
+		if (state[i].load_offset == VM_PLANES_UNSET_VALUE ||
+		    state[i].memory_size == VM_PLANES_UNSET_VALUE ||
+		    !state[i].kernel[0])
+			return -EINVAL;
+	}
+
+	kfree(state);
+	return 0;
+}
+
+/* ---- Config loading via VFS ---- */
+
+static int __init vm_planes_get_cfg(unsigned int *plane_count,
+				    struct vm_plane_config **plane_cfg)
+{
+	void *buf;
+	loff_t size;
+	int ret;
+
+	ret = vm_planes_read_file("/" VM_PLANES_CONFIG_FILE, &buf, &size);
+	if (ret) {
+		pr_err("vm_planes: cannot read /%s: %d\n",
+		       VM_PLANES_CONFIG_FILE, ret);
+		return ret;
+	}
+
+	ret = parse_vm_planes_kconfig(buf, (size_t)size, plane_count, plane_cfg);
+	kvfree(buf);
+	return ret;
+}
+
+/* ---- Kernel loading ---- */
+
+static int __init copy_to_early_mem(phys_addr_t dest, const void *src,
+				    unsigned long size)
+{
+	void *p;
+
+	if (!size)
+		return 0;
+	p = memremap(dest, size, MEMREMAP_WB);
+	if (!p)
+		return -ENOMEM;
+	memcpy(p, src, size);
+	memunmap(p);
+	return 0;
+}
+
+static int __init zero_early_mem(phys_addr_t dest, unsigned long size)
+{
+	void *p;
+
+	if (!size)
+		return 0;
+	p = memremap(dest, size, MEMREMAP_WB);
+	if (!p)
+		return -ENOMEM;
+	memset(p, 0, size);
+	memunmap(p);
+	return 0;
+}
+
+static int __init load_plane_kernel_elf(const u8 *data, u32 size,
+					struct vm_plane_config *cfg)
+{
+	const Elf64_Ehdr *ehdr;
+	const Elf64_Phdr *phdr;
+	unsigned int i;
+	int ret;
+
+	if (size < sizeof(*ehdr)) {
+		pr_err("vm_planes: ELF image too small (%u bytes)\n", size);
+		return -EINVAL;
+	}
+
+	ehdr = (const Elf64_Ehdr *)data;
+
+	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
+		pr_err("vm_planes: not a valid ELF image\n");
+		return -EINVAL;
+	}
+
+	if (ehdr->e_ident[EI_CLASS] != ELFCLASS64 ||
+	    ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||
+	    ehdr->e_type != ET_EXEC ||
+	    ehdr->e_machine != EM_X86_64) {
+		pr_err("vm_planes: unsupported ELF format (need x86_64 ET_EXEC LE)\n");
+		return -EINVAL;
+	}
+
+	if (!ehdr->e_phnum || ehdr->e_phentsize != sizeof(Elf64_Phdr)) {
+		pr_err("vm_planes: invalid ELF program headers\n");
+		return -EINVAL;
+	}
+
+	if (ehdr->e_phoff + (u64)ehdr->e_phnum * sizeof(Elf64_Phdr) > size) {
+		pr_err("vm_planes: ELF program headers extend beyond file\n");
+		return -EINVAL;
+	}
+
+	phdr = (const Elf64_Phdr *)(data + ehdr->e_phoff);
+
+	for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+		phys_addr_t dest;
+		u64 bss_size;
+
+		if (phdr->p_type != PT_LOAD)
+			continue;
+
+		if (!phdr->p_memsz)
+			continue;
+
+		/*
+		 * Bias the ELF physical address by load_offset so that the
+		 * kernel's link-time p_paddr values are treated as offsets
+		 * within the plane's memory region.
+		 */
+		dest = cfg->load_offset + phdr->p_paddr;
+
+		if (dest < cfg->load_offset ||
+		    dest + phdr->p_memsz > cfg->load_offset + cfg->memory_size) {
+			pr_err("vm_planes: ELF PT_LOAD at 0x%llx+0x%llx outside plane [0x%llx..0x%llx]\n",
+			       (unsigned long long)dest,
+			       (unsigned long long)phdr->p_memsz,
+			       (unsigned long long)cfg->load_offset,
+			       (unsigned long long)(cfg->load_offset + cfg->memory_size));
+			return -EINVAL;
+		}
+
+		if (phdr->p_offset + phdr->p_filesz > size) {
+			pr_err("vm_planes: ELF PT_LOAD file data beyond image\n");
+			return -EINVAL;
+		}
+
+		if (phdr->p_filesz) {
+			ret = copy_to_early_mem(dest, data + phdr->p_offset,
+						phdr->p_filesz);
+			if (ret)
+				return ret;
+		}
+
+		bss_size = phdr->p_memsz - phdr->p_filesz;
+		if (bss_size) {
+			ret = zero_early_mem(dest + phdr->p_filesz, bss_size);
+			if (ret)
+				return ret;
+		}
+
+		/*
+		 * Compute the physical entry point: if e_entry falls within
+		 * this segment's virtual range, convert vaddr→paddr and bias.
+		 * Also handle kernels where e_entry is already a physical
+		 * address by checking the p_paddr range as a fallback.
+		 */
+		if (ehdr->e_entry >= phdr->p_vaddr &&
+		    ehdr->e_entry < phdr->p_vaddr + phdr->p_memsz)
+			cfg->entry_point = cfg->load_offset +
+				phdr->p_paddr + (ehdr->e_entry - phdr->p_vaddr);
+		else if (ehdr->e_entry >= phdr->p_paddr &&
+			 ehdr->e_entry < phdr->p_paddr + phdr->p_memsz)
+			cfg->entry_point = cfg->load_offset + ehdr->e_entry;
+
+		pr_info("vm_planes: ELF PT_LOAD: paddr=0x%llx filesz=0x%llx memsz=0x%llx\n",
+			(unsigned long long)dest,
+			(unsigned long long)phdr->p_filesz,
+			(unsigned long long)phdr->p_memsz);
+	}
+
+	if (!cfg->entry_point) {
+		pr_err("vm_planes: ELF entry point 0x%llx not in any PT_LOAD segment\n",
+		       (unsigned long long)ehdr->e_entry);
+		return -EINVAL;
+	}
+	pr_info("vm_planes: ELF entry point: 0x%llx (virt 0x%llx)\n",
+		(unsigned long long)cfg->entry_point,
+		(unsigned long long)ehdr->e_entry);
+
+	return 0;
+}
+
+static int __init load_plane_kernel_raw(const u8 *data, u32 size,
+					struct vm_plane_config *cfg)
+{
+	if (size > cfg->memory_size) {
+		pr_err("vm_planes: raw kernel image (%u bytes) exceeds plane memory (%llu bytes)\n",
+		       size, (unsigned long long)cfg->memory_size);
+		return -ENOMEM;
+	}
+
+	cfg->entry_point = cfg->load_offset;
+	return copy_to_early_mem(cfg->load_offset, data, size);
+}
+
+int __init load_vm_plane_kernels(unsigned int plane_count,
+				 struct vm_plane_config *plane_cfg)
+{
+	unsigned int i;
+	int err = 0;
+
+	for (i = 1; i < plane_count; i++) {
+		void *data;
+		loff_t fsize;
+		int ret;
+
+		ret = vm_planes_read_file(plane_cfg[i].kernel, &data, &fsize);
+		if (ret) {
+			pr_err("vm_planes: plane %u: kernel '%s' not found: %d\n",
+			       i, plane_cfg[i].kernel, ret);
+			err = ret;
+			continue;
+		}
+
+		switch (plane_cfg[i].kernel_format) {
+		case VM_PLANE_KFMT_RAW:
+			ret = load_plane_kernel_raw(data, (u32)fsize,
+						    &plane_cfg[i]);
+			break;
+		case VM_PLANE_KFMT_ELF:
+			ret = load_plane_kernel_elf(data, (u32)fsize,
+						    &plane_cfg[i]);
+			break;
+		case VM_PLANE_KFMT_BZIMAGE:
+			pr_err("vm_planes: plane %u: bzImage format not yet supported\n",
+			       i);
+			err = -ENOSYS;
+			kvfree(data);
+			continue;
+		default:
+			pr_err("vm_planes: plane %u: unknown kernel format %u\n",
+			       i, plane_cfg[i].kernel_format);
+			err = -EINVAL;
+			kvfree(data);
+			continue;
+		}
+
+		if (ret) {
+			pr_err("vm_planes: plane %u: failed to load kernel: %d\n",
+			       i, ret);
+			err = ret;
+		} else {
+			pr_info("vm_planes: plane %u: loaded '%s' (%lld bytes) at 0x%llx\n",
+				i, plane_cfg[i].kernel, fsize,
+				(unsigned long long)plane_cfg[i].load_offset);
+		}
+
+		kvfree(data);
+	}
+
+	return err;
+}
+
+/* ---- Activation ---- */
+
+int __init __weak alloc_vm_planes(unsigned int plane_count,
+				   struct vm_plane_config *plane_cfg) { return -ENOSYS; }
+
+int __init __weak activate_vm_planes(unsigned int plane_count,
+				      struct vm_plane_config *plane_cfg) { return -ENOSYS; }
+
+/*
+ * Set up VM planes during boot.
+ *
+ * Invoked from the VBS enable path at late_initcall: after device drivers
+ * have initialised (so the rootfs is populated and the plane config and
+ * kernels can be read) and before userspace starts, so the secure plane
+ * vcpu exists by the time the first VTL call is issued.
+ */
+int __init vm_planes_bootstrap(void)
+{
+	unsigned int plane_count = VM_PLANES_DEFAULT_COUNT;
+	struct vm_plane_config *plane_cfg;
+	int ret;
+
+	/* Ensure any asynchronous initramfs unpacking has completed. */
+	wait_for_initramfs();
+
+	if (!kvm_para_available()) {
+		pr_info("vm_planes: KVM paravirt unavailable, skipping plane setup\n");
+		return -ENODEV;
+	}
+
+	ret = vm_planes_get_cfg(&plane_count, &plane_cfg);
+	if (ret) {
+		pr_warn("vm_planes: failed to parse %s: %d\n",
+			VM_PLANES_CONFIG_FILE, ret);
+		return ret;
+	}
+
+	pr_info("vm_planes: enabling %u planes (ids 0..%u)\n",
+		plane_count, plane_count - 1);
+
+	ret = alloc_vm_planes(plane_count, plane_cfg);
+	if (ret) {
+		pr_err("vm_planes: failed to allocate planes: %d\n", ret);
+		return ret;
+	}
+
+	ret = load_vm_plane_kernels(plane_count, plane_cfg);
+	if (ret) {
+		pr_err("vm_planes: failed to load plane kernels: %d\n", ret);
+		return ret;
+	}
+
+	ret = activate_vm_planes(plane_count, plane_cfg);
+	if (ret)
+		pr_err("vm_planes: failed to activate planes: %d\n", ret);
+
+	return ret;
+}
+
+#endif /* CONFIG_VM_PLANES */
-- 
2.55.0


  parent reply	other threads:[~2026-08-11  1:53 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-11  1:52 [RFC PATCH v2 0/8] VBS/VSM-on-KVM: guest support using VM Planes Sriram Nambakam
2026-08-11  1:52 ` [RFC PATCH v2 1/8] KVM: x86: raise the default maximum planes to two Sriram Nambakam
2026-08-11  1:52 ` [RFC PATCH v2 2/8] security/vbs: introduce core VBS framework Sriram Nambakam
2026-08-11  1:52 ` [RFC PATCH v2 3/8] security/vbs: add platform probe and backend registration Sriram Nambakam
2026-08-11  1:52 ` [RFC PATCH v2 4/8] security/vbs: add KVM software planes backend Sriram Nambakam
2026-08-11  1:52 ` [RFC PATCH v2 5/8] security/vbs: enable the backend after driver init Sriram Nambakam
2026-08-11  1:52 ` Sriram Nambakam [this message]
2026-08-11  1:52 ` [RFC PATCH v2 7/8] security/vbs: bootstrap the plane from the enable path Sriram Nambakam
2026-08-11  1:52 ` [RFC PATCH v2 8/8] drivers/virt: add KVM VM-planes secure-plane monitor Sriram Nambakam

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=20260811015243.188486-7-snambakam@linux.microsoft.com \
    --to=snambakam@linux.microsoft.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    /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