From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 167BA429CC7; Wed, 5 Aug 2026 11:03:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=13.77.154.182 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785927828; cv=none; b=Sr6IYKbJPyad8rMRBKoIzwD/NQ58CPoZv4Tx81b3rOu2jlmzFYvLR6SX6b5NTv5yrN9WdzSR75GlpvxmaAHj73h/7+4Lc7zng0uPvH7ttb0CGYtOfZ+6jg1PSic1yNb51OeTG0hV6mYSu5Yynf63Wf5/VufqVPuXetT/ryH+zss= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785927828; c=relaxed/simple; bh=UVA6bsrvHu8PAPd6wcbwv+Ktpzqk0/zXguvpb1dB0QE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZhUaln4IbnrZ9H00QjKbm0DRsFuKbB6MSmSSkQVPeNGjrPhaWW/pZOky2TN+65I1q/27m5M+9cni272di9/ljZXBaLFx1abLMG1MM4b0/k76UYTp7aPaEfr/bTG7/ujj/a0BjQqeb1Us0kCxjzK7av9sTMhTFqEQ9uIEL67qcvg= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com; spf=pass smtp.mailfrom=linux.microsoft.com; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b=aiK/KsTS; arc=none smtp.client-ip=13.77.154.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.microsoft.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.microsoft.com header.i=@linux.microsoft.com header.b="aiK/KsTS" Received: from fedora.hsd1.wa.comcast.net (unknown [52.148.140.42]) by linux.microsoft.com (Postfix) with ESMTPSA id 77FE920B716B; Wed, 5 Aug 2026 04:03:25 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 77FE920B716B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1785927805; bh=AJ1UHw5uzjpw7l7HTWL+cTQ09mxfzq4yQBen6QoG6XA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aiK/KsTSHjCk0YpDVsLehFqSa82TGSvx5cYUNCUv7+oe5dj7OkOhKDwXcm0/vmVCy F2m69HpewmZ0gh0aRKyBUonPTyG4rxqqiTipBtBJfSrrTFvqPjSVWq+whSibEqG3U7 1rxEouF7SdS40Igs+bG2ZC4zwWv3PI6EWSt3USns= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v1 05/42] Initial support for VM Planes - Add kernel config for CONFIG_VM_PLANES - Parse vm plane config from initrd for plane configuration - Make hypercalls to allocate memory for the vm planes. Date: Wed, 5 Aug 2026 04:02:47 -0700 Message-ID: <20260805110324.25067-6-snambakam@linux.microsoft.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260805110324.25067-1-snambakam@linux.microsoft.com> References: <20260805110324.25067-1-snambakam@linux.microsoft.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit --- arch/x86/include/asm/cpu.h | 8 + arch/x86/kernel/cpu/common.c | 38 ++++ include/linux/vm_planes.h | 22 ++ init/Kconfig | 16 ++ init/Makefile | 1 + init/main.c | 4 + init/vm_planes.c | 417 +++++++++++++++++++++++++++++++++++ 7 files changed, 506 insertions(+) create mode 100644 include/linux/vm_planes.h create mode 100644 init/vm_planes.c diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index 57a0786dfd75..8ab76adf14a9 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -4,11 +4,19 @@ #include #include +#include #include #include #include #include +#ifdef CONFIG_VM_PLANES +struct vm_plane_config; + +void __init alloc_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg); +#endif + #ifndef CONFIG_SMP #define cpu_physical_id(cpu) boot_cpu_physical_apicid #endif /* CONFIG_SMP */ diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index a3df21d26460..166597204739 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -20,11 +20,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -32,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -77,6 +80,11 @@ #include "cpu.h" +#ifdef CONFIG_VM_PLANES +/* Private hypercall number for early VM plane configuration. */ +#define KVM_HC_VM_PLANES_CONFIG 0x1000 +#endif + DEFINE_PER_CPU_READ_MOSTLY(struct cpuinfo_x86, cpu_info); EXPORT_PER_CPU_SYMBOL(cpu_info); @@ -2664,3 +2672,33 @@ void __init arch_cpu_finalize_init(void) */ mem_encrypt_init(); } + +#ifdef CONFIG_VM_PLANES +void __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; + + if (!kvm_para_available()) { + pr_warn("vm_planes: hypercall interface unavailable\n"); + return; + } + + 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; + } + + 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); +} +#endif diff --git a/include/linux/vm_planes.h b/include/linux/vm_planes.h new file mode 100644 index 000000000000..4a91dc79b7ba --- /dev/null +++ b/include/linux/vm_planes.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_VM_PLANES_H +#define _LINUX_VM_PLANES_H + +#include +#include + +#ifdef CONFIG_VM_PLANES + +#define VM_PLANE_KERNEL_NAME_MAX 128 + +struct vm_plane_config { + phys_addr_t load_offset; + phys_addr_t memory_size; + char kernel[VM_PLANE_KERNEL_NAME_MAX]; +}; + +void __init arch_init_vm_planes(void); + +#endif /* CONFIG_VM_PLANES */ + +#endif /* _LINUX_VM_PLANES_H */ diff --git a/init/Kconfig b/init/Kconfig index 10f2013b5321..23d9cca334ba 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1713,6 +1713,22 @@ menuconfig EXPERT environments which can tolerate a "non-standard" kernel. Only use this if you really know what you are doing. +config VM_PLANES + bool "Enable VM planes early boot support" if EXPERT + default n + help + Enable hypervisor enabled multi-kernel support. + + This allows processing the kernel command-line parameter + "enable-vm-planes" and, when requested, calling + arch_init_vm_planes() during start_kernel(). + + The initrd config-vm-planes file is expected to provide per-plane + entries for PLANE__KERNEL, PLANE__LOAD_OFFSET, and + PLANE__MEMORY_SIZE. + + If unsure, say N. + config UID16 bool "Enable 16-bit UID system calls" if EXPERT depends on HAVE_UID16 && MULTIUSER diff --git a/init/Makefile b/init/Makefile index d6f75d8907e0..113133c8cdd7 100644 --- a/init/Makefile +++ b/init/Makefile @@ -6,6 +6,7 @@ ccflags-y := -fno-function-sections -fno-data-sections obj-y := main.o version.o mounts.o +obj-y += vm_planes.o ifneq ($(CONFIG_BLK_DEV_INITRD),y) obj-y += noinitramfs.o else diff --git a/init/main.c b/init/main.c index e363232b428b..3e35c2caca17 100644 --- a/init/main.c +++ b/init/main.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -993,6 +994,9 @@ void start_kernel(void) pr_notice("%s", linux_banner); setup_arch(&command_line); mm_core_init_early(); +#ifdef CONFIG_VM_PLANES + arch_init_vm_planes(); +#endif /* Static keys and static calls are needed by LSMs */ jump_label_init(); static_call_init(); diff --git a/init/vm_planes.c b/init/vm_planes.c new file mode 100644 index 000000000000..30f17da92332 --- /dev/null +++ b/init/vm_planes.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_VM_PLANES +static bool __initdata enable_vm_planes_requested; + +#define VM_PLANES_CONFIG_FILE "config-vm-planes" +#define VM_PLANES_DEFAULT_COUNT 1 + +struct vm_plane_parse_state { + phys_addr_t have_load_offset; + phys_addr_t have_memory_size; + char have_kernel[VM_PLANE_KERNEL_NAME_MAX]; +}; + +#define VM_PLANES_UNSET_VALUE ((phys_addr_t)~0) + +struct cpio_newc_header { + char c_magic[6]; + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +}; + +static int __init parse_hex_field(const char *field, size_t len, u32 *value) +{ + u32 v = 0; + size_t i; + + for (i = 0; i < len; i++) { + u8 c = field[i]; + + v <<= 4; + if (c >= '0' && c <= '9') + v |= c - '0'; + else if (c >= 'a' && c <= 'f') + v |= c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + v |= c - 'A' + 10; + else + return -EINVAL; + } + + *value = v; + return 0; +} + +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[192]; + 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 -EINVAL; + *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].have_kernel, val, + sizeof(state[plane_id].have_kernel)); + 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].have_load_offset = parsed; + return 0; + } + + if (!strcmp(key, "MEMORY_SIZE")) { + plane_cfg[plane_id].memory_size = parsed; + state[plane_id].have_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 = memblock_alloc(*plane_count * sizeof(**plane_cfg), + SMP_CACHE_BYTES); + if (!*plane_cfg) + return -ENOMEM; + + state = memblock_alloc(*plane_count * sizeof(*state), SMP_CACHE_BYTES); + if (!state) + return -ENOMEM; + + memset(*plane_cfg, 0, *plane_count * sizeof(**plane_cfg)); + for (i = 0; i < *plane_count; i++) { + state[i].have_load_offset = VM_PLANES_UNSET_VALUE; + state[i].have_memory_size = VM_PLANES_UNSET_VALUE; + state[i].have_kernel[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 = 0; i < *plane_count; i++) { + if (state[i].have_load_offset == VM_PLANES_UNSET_VALUE || + state[i].have_memory_size == VM_PLANES_UNSET_VALUE || + !state[i].have_kernel[0]) + return -EINVAL; + } + + return 0; +} + +static bool __init cpio_name_match(const char *name, size_t namesize, + const char *target) +{ + while (namesize > 1 && (*name == '/' || + (namesize > 2 && name[0] == '.' && name[1] == '/'))) { + if (*name == '/') { + name++; + namesize--; + } else { + name += 2; + namesize -= 2; + } + } + + return !strncmp(name, target, namesize - 1) && + strlen(target) == namesize - 1; +} + +static int __init vm_planes_get_cfg_from_initrd(unsigned int *plane_count, + struct vm_plane_config **plane_cfg) +{ + const u8 *p = (const u8 *)(unsigned long)initrd_start; + const u8 *end = (const u8 *)(unsigned long)initrd_end; + + if (!initrd_start || !initrd_end || initrd_end <= initrd_start) + return -ENOENT; + + while (p + sizeof(struct cpio_newc_header) <= end) { + const struct cpio_newc_header *hdr; + const char *name; + const u8 *data; + u32 namesize, filesize; + u32 name_align, data_align; + int ret; + + hdr = (const struct cpio_newc_header *)p; + if (memcmp(hdr->c_magic, "070701", 6) && + memcmp(hdr->c_magic, "070702", 6)) + return -EINVAL; + + ret = parse_hex_field(hdr->c_namesize, sizeof(hdr->c_namesize), &namesize); + if (ret) + return ret; + + ret = parse_hex_field(hdr->c_filesize, sizeof(hdr->c_filesize), &filesize); + if (ret) + return ret; + + if (!namesize) + return -EINVAL; + + p += sizeof(*hdr); + if (p + namesize > end) + return -EINVAL; + + name = (const char *)p; + name_align = ALIGN(namesize, 4); + if (p + name_align > end) + return -EINVAL; + + data = p + name_align; + if (data + filesize > end) + return -EINVAL; + + if (!strcmp(name, "TRAILER!!!")) + break; + + if (cpio_name_match(name, namesize, VM_PLANES_CONFIG_FILE)) + return parse_vm_planes_kconfig((const char *)data, + filesize, + plane_count, + plane_cfg); + + data_align = ALIGN(filesize, 4); + if (data + data_align < data || data + data_align > end) + return -EINVAL; + + p = data + data_align; + } + + return -ENOENT; +} + +static int __init parse_enable_vm_planes(char *str) +{ + bool enable; + + if (!str) { + enable_vm_planes_requested = true; + return 0; + } + + if (kstrtobool(str, &enable)) + return -EINVAL; + + enable_vm_planes_requested = enable; + return 0; +} + +early_param("enable-vm-planes", parse_enable_vm_planes); + +void __init __weak alloc_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg) { } + +void __init arch_init_vm_planes(void) +{ + unsigned int plane_count = VM_PLANES_DEFAULT_COUNT; + struct vm_plane_config *plane_cfg; + + if (!enable_vm_planes_requested) + return; + + if (!kvm_para_available()) + return; + + if (vm_planes_get_cfg_from_initrd(&plane_count, &plane_cfg)) { + pr_warn("vm_planes: failed to parse %s from initrd\n", + VM_PLANES_CONFIG_FILE); + return; + } + + pr_info("vm_planes: enabling %u planes (ids 0..%u)\n", + plane_count, plane_count - 1); + alloc_vm_planes(plane_count, plane_cfg); +} + +#endif /* CONFIG_VM_PLANES */ -- 2.55.0