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 6F31B43802B; Wed, 5 Aug 2026 11:03:52 +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=1785927834; cv=none; b=X099STt5dKxJdheXuOXhSAVMUWMr/8S8HIy1CVhVH5cCiuTCDHbKAQWwsnexVksrdYmdp0qMYX1BiHucI9SxzYJkztlpBl09Tl2Trh/wOZxOBQqakTlgP+k+5elJrZ8yKDsBZmVP51cnGR9NDD7f1DFLFEt6gdSL3ZgvnyjOZOw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785927834; c=relaxed/simple; bh=D1PdRGy+W/GQGTigJBXozi74E4Z8ucqMCmtco5SiarI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=fGFdEGO636Kp50hHh4gNXl9Nv0LYFbRLB2spCaGWNoXnoPF3jvEwi2B0TITkv1C7R5s1iY7vlx+uy/vy4HDw9Sum7hILyknSPVnMSyYkLTsWWT9hpVlVqugKEYsgkE86k+jaD501ZMbG8UZaLDtMHI+fAAf8jTvCU7+vW8Mckdo= 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=lD4SuauX; 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="lD4SuauX" Received: from fedora.hsd1.wa.comcast.net (unknown [52.148.140.42]) by linux.microsoft.com (Postfix) with ESMTPSA id C91AD20B7169; Wed, 5 Aug 2026 04:03:30 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com C91AD20B7169 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1785927811; bh=pr71ZzUrlyCcrAnUnK+RK3cbvl/A+yinC6vkxx+FmC0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lD4SuauXyYGIKHkk1tgK6kDc63dQQszgfO/YfAri2idrkveF7EeypcBMuwk0pnGjh SJl1HwpiCTwmqzOiBy8aA2ITxeDv5Ig5KGSC4t7SdCo83y4SJU17HNHXkyXlK54+lq EBCYFpf/LbKfAf50Q9tgxXsuORb/8OWFpf+XxQsw= From: Sriram Nambakam To: kvm@vger.kernel.org Cc: linux-kernel@vger.kernel.org Subject: [RFC PATCH v1 11/42] Various changes to support VM Planes. Date: Wed, 5 Aug 2026 04:02:53 -0700 Message-ID: <20260805110324.25067-12-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-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove custom parsing of initrd. 1. **vm_planes.c** — Core VM planes implementation (major rewrite) - Fixed config parser: `PLANE_COUNT=` line no longer causes fatal `-EINVAL` (returns `-ENOENT` to skip) - ELF loader: biases `p_paddr` by `load_offset` so kernel loads at correct GPA - Entry point: computes physical entry from ELF vaddr→paddr mapping, with fallback for physical `e_entry` - `activate_vm_planes()` now passes `plane_cfg` GPA so QEMU can read the updated `entry_point` 2. **vm_planes.h** — Added `entry_point` field declaration 3. **main.c** — Minor adjustment to `arch_init_vm_planes()` call site 4. **common.c** — KVM hypercall implementations - Removed hardcoded `0x1000`/`0x1001` hypercall numbers - `alloc_vm_planes()`: unchanged (uses correct HC numbers from UAPI header) - `activate_vm_planes()`: now passes `plane_cfg` GPA + `plane_count` (was just `plane_count`) 5. **cpu.h** — Updated `activate_vm_planes()` signature to include `plane_cfg` 6. **x86.c** — KVM host-side hypercall support - `KVM_EXIT_HYPERCALL_VALID_MASK`: added bits 13 and 14 for VM planes hypercalls - Added `KVM_HC_VM_PLANES_CONFIG` and `KVM_HC_VM_PLANES_ACTIVATE` case handlers that exit to userspace (QEMU) 7. **kvm_para.h** — Added hypercall numbers - `KVM_HC_VM_PLANES_CONFIG = 13` - `KVM_HC_VM_PLANES_ACTIVATE = 14` --- arch/x86/include/asm/cpu.h | 3 +- arch/x86/kernel/cpu/common.c | 19 +- arch/x86/kvm/x86.c | 25 +- include/linux/vm_planes.h | 1 + include/uapi/linux/kvm_para.h | 2 + init/main.c | 7 +- init/vm_planes.c | 422 ++++++++++++++++++---------------- 7 files changed, 266 insertions(+), 213 deletions(-) diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index 52e80c6ac8f0..f9cb541e6367 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -15,7 +15,8 @@ struct vm_plane_config; int __init alloc_vm_planes(unsigned int plane_count, struct vm_plane_config *plane_cfg); -int __init activate_vm_planes(unsigned int plane_count); +int __init activate_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg); #endif #ifndef CONFIG_SMP diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 9912208d2010..7edc2c4072cc 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -80,13 +80,6 @@ #include "cpu.h" -#ifdef CONFIG_VM_PLANES -/* Private hypercall number for early VM plane configuration. */ -#define KVM_HC_VM_PLANES_CONFIG 0x1000 -/* Private hypercall number to activate all configured planes. */ -#define KVM_HC_VM_PLANES_ACTIVATE 0x1001 -#endif - DEFINE_PER_CPU_READ_MOSTLY(struct cpuinfo_x86, cpu_info); EXPORT_PER_CPU_SYMBOL(cpu_info); @@ -2708,11 +2701,13 @@ int __init alloc_vm_planes(unsigned int plane_count, return 0; } -int __init activate_vm_planes(unsigned int plane_count) +int __init activate_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg) { + phys_addr_t phys; long ret; - if (!plane_count) + if (!plane_count || !plane_cfg) return -EINVAL; if (!kvm_para_available()) { @@ -2720,7 +2715,11 @@ int __init activate_vm_planes(unsigned int plane_count) return -ENODEV; } - ret = kvm_hypercall1(KVM_HC_VM_PLANES_ACTIVATE, plane_count); + 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; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index a0a8818b3096..b7256f155bea 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -119,7 +119,9 @@ u64 __read_mostly efer_reserved_bits = ~((u64)(EFER_SCE | EFER_LME | EFER_LMA)); static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE); #endif -#define KVM_EXIT_HYPERCALL_VALID_MASK (1 << KVM_HC_MAP_GPA_RANGE) +#define KVM_EXIT_HYPERCALL_VALID_MASK (BIT(KVM_HC_MAP_GPA_RANGE) | \ + BIT(KVM_HC_VM_PLANES_CONFIG) | \ + BIT(KVM_HC_VM_PLANES_ACTIVATE)) #define KVM_CAP_PMU_VALID_MASK KVM_PMU_CAP_DISABLE @@ -10530,6 +10532,27 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl, vcpu->arch.complete_userspace_io = complete_hypercall; return 0; } + case KVM_HC_VM_PLANES_CONFIG: + case KVM_HC_VM_PLANES_ACTIVATE: { + ret = -KVM_ENOSYS; + if (!user_exit_on_hypercall(vcpu->kvm, nr)) + break; + + vcpu->run->exit_reason = KVM_EXIT_HYPERCALL; + vcpu->run->hypercall.nr = nr; + vcpu->run->hypercall.ret = 0; + vcpu->run->hypercall.args[0] = a0; + vcpu->run->hypercall.args[1] = a1; + vcpu->run->hypercall.args[2] = a2; + vcpu->run->hypercall.args[3] = a3; + vcpu->run->hypercall.flags = 0; + if (op_64_bit) + vcpu->run->hypercall.flags |= KVM_EXIT_HYPERCALL_LONG_MODE; + + WARN_ON_ONCE(vcpu->run->hypercall.flags & KVM_EXIT_HYPERCALL_MBZ); + vcpu->arch.complete_userspace_io = complete_hypercall; + return 0; + } default: ret = -KVM_ENOSYS; break; diff --git a/include/linux/vm_planes.h b/include/linux/vm_planes.h index bb06dcbcf0cb..47f05fa80039 100644 --- a/include/linux/vm_planes.h +++ b/include/linux/vm_planes.h @@ -19,6 +19,7 @@ enum vm_plane_kernel_format { struct vm_plane_config { phys_addr_t load_offset; phys_addr_t memory_size; + phys_addr_t entry_point; unsigned int vcpu_count; unsigned int kernel_format; char kernel[VM_PLANE_KERNEL_NAME_MAX]; diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index 960c7e93d1a9..1b097f7ed937 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h @@ -30,6 +30,8 @@ #define KVM_HC_SEND_IPI 10 #define KVM_HC_SCHED_YIELD 11 #define KVM_HC_MAP_GPA_RANGE 12 +#define KVM_HC_VM_PLANES_CONFIG 13 +#define KVM_HC_VM_PLANES_ACTIVATE 14 /* * hypercalls use architecture specific diff --git a/init/main.c b/init/main.c index 3e35c2caca17..1c779f6d60cc 100644 --- a/init/main.c +++ b/init/main.c @@ -994,9 +994,6 @@ 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(); @@ -1666,6 +1663,10 @@ static noinline void __init kernel_init_freeable(void) wait_for_initramfs(); console_on_rootfs(); +#ifdef CONFIG_VM_PLANES + arch_init_vm_planes(); +#endif + /* * check if there is an early userspace init. If yes, let it do all * the work diff --git a/init/vm_planes.c b/init/vm_planes.c index 613daa161298..274c0015fe76 100644 --- a/init/vm_planes.c +++ b/init/vm_planes.c @@ -3,12 +3,15 @@ #include #include #include -#include +#include #include #include +#include +#include #include #include #include +#include #include #include #include @@ -30,46 +33,49 @@ struct vm_plane_parse_state { #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) +/* + * 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) { - 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; + 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; } - *value = v; + *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) { @@ -174,7 +180,7 @@ static int __init parse_plane_cfg_line(const char *line, size_t len, key = strchr(p, '_'); if (!key) - return -EINVAL; + return -ENOENT; *key++ = '\0'; if (kstrtouint(p, 10, &plane_id) || plane_id >= plane_count) @@ -297,16 +303,14 @@ static int __init parse_vm_planes_kconfig(const char *buf, size_t len, if (*plane_count > UINT_MAX / sizeof(**plane_cfg)) return -E2BIG; - *plane_cfg = memblock_alloc(*plane_count * sizeof(**plane_cfg), - SMP_CACHE_BYTES); + *plane_cfg = kzalloc(*plane_count * sizeof(**plane_cfg), GFP_KERNEL); if (!*plane_cfg) return -ENOMEM; - state = memblock_alloc(*plane_count * sizeof(*state), SMP_CACHE_BYTES); + state = kzalloc(*plane_count * sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; - memset(*plane_cfg, 0, *plane_count * sizeof(**plane_cfg)); for (i = 0; i < *plane_count; i++) { state[i].load_offset = VM_PLANES_UNSET_VALUE; state[i].memory_size = VM_PLANES_UNSET_VALUE; @@ -329,9 +333,6 @@ static int __init parse_vm_planes_kconfig(const char *buf, size_t len, p++; } - /* Plane 0 is the already-running boot plane; only secondary planes - * must provide full allocation metadata. - */ for (i = 1; i < *plane_count; i++) { if (state[i].load_offset == VM_PLANES_UNSET_VALUE || state[i].memory_size == VM_PLANES_UNSET_VALUE || @@ -340,179 +341,192 @@ static int __init parse_vm_planes_kconfig(const char *buf, size_t len, return -EINVAL; } + kfree(state); 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; - } - } +/* ---- Config loading via VFS ---- */ - 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) +static int __init vm_planes_get_cfg(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; + void *buf; + loff_t size; + int ret; - p += sizeof(*hdr); - if (p + namesize > end) - return -EINVAL; + 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; + } - name = (const char *)p; - name_align = ALIGN(namesize, 4); - if (p + name_align > end) - return -EINVAL; + ret = parse_vm_planes_kconfig(buf, (size_t)size, plane_count, plane_cfg); + kvfree(buf); + return ret; +} - data = p + name_align; - if (data + filesize > end) - return -EINVAL; +/* ---- Kernel loading ---- */ - if (!strcmp(name, "TRAILER!!!")) - break; +static int __init copy_to_early_mem(phys_addr_t dest, const void *src, + unsigned long size) +{ + unsigned long slop, clen; + char *p; - if (cpio_name_match(name, namesize, VM_PLANES_CONFIG_FILE)) - return parse_vm_planes_kconfig((const char *)data, - filesize, - plane_count, - plane_cfg); + while (size) { + slop = offset_in_page(dest); + clen = size; + if (clen > PAGE_SIZE - slop) + clen = PAGE_SIZE - slop; + p = early_memremap(dest & PAGE_MASK, clen + slop); + if (!p) + return -ENOMEM; + memcpy(p + slop, src, clen); + early_memunmap(p, clen + slop); + dest += clen; + src += clen; + size -= clen; + } + return 0; +} - data_align = ALIGN(filesize, 4); - if (data + data_align < data || data + data_align > end) - return -EINVAL; +static int __init zero_early_mem(phys_addr_t dest, unsigned long size) +{ + unsigned long slop, clen; + char *p; - p = data + data_align; + while (size) { + slop = offset_in_page(dest); + clen = size; + if (clen > PAGE_SIZE - slop) + clen = PAGE_SIZE - slop; + p = early_memremap(dest & PAGE_MASK, clen + slop); + if (!p) + return -ENOMEM; + memset(p + slop, 0, clen); + early_memunmap(p, clen + slop); + dest += clen; + size -= clen; } - - return -ENOENT; + return 0; } -static int __init find_initrd_file(const char *filename, - const u8 **out_data, u32 *out_size) +static int __init load_plane_kernel_elf(const u8 *data, u32 size, + struct vm_plane_config *cfg) { - const u8 *p = (const u8 *)(unsigned long)initrd_start; - const u8 *end = (const u8 *)(unsigned long)initrd_end; + const Elf64_Ehdr *ehdr; + const Elf64_Phdr *phdr; + unsigned int i; + int ret; - if (!initrd_start || !initrd_end || initrd_end <= initrd_start) - return -ENOENT; + if (size < sizeof(*ehdr)) { + pr_err("vm_planes: ELF image too small (%u bytes)\n", size); + return -EINVAL; + } - 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; + ehdr = (const Elf64_Ehdr *)data; - hdr = (const struct cpio_newc_header *)p; - if (memcmp(hdr->c_magic, "070701", 6) && - memcmp(hdr->c_magic, "070702", 6)) - return -EINVAL; + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + pr_err("vm_planes: not a valid ELF image\n"); + return -EINVAL; + } - ret = parse_hex_field(hdr->c_namesize, - sizeof(hdr->c_namesize), &namesize); - if (ret) - return ret; + 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; + } - ret = parse_hex_field(hdr->c_filesize, - sizeof(hdr->c_filesize), &filesize); - if (ret) - return ret; + if (!ehdr->e_phnum || ehdr->e_phentsize != sizeof(Elf64_Phdr)) { + pr_err("vm_planes: invalid ELF program headers\n"); + return -EINVAL; + } - if (!namesize) - 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; + } - p += sizeof(*hdr); - if (p + namesize > end) - return -EINVAL; + phdr = (const Elf64_Phdr *)(data + ehdr->e_phoff); - name = (const char *)p; - name_align = ALIGN(namesize, 4); - if (p + name_align > end) - return -EINVAL; + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + phys_addr_t dest; + u64 bss_size; - data = p + name_align; - if (data + filesize > end) - return -EINVAL; + if (phdr->p_type != PT_LOAD) + continue; - if (!strcmp(name, "TRAILER!!!")) - break; + if (!phdr->p_memsz) + continue; - if (cpio_name_match(name, namesize, filename)) { - *out_data = data; - *out_size = filesize; - return 0; + /* + * 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; } - data_align = ALIGN(filesize, 4); - if (data + data_align < data || data + data_align > end) + if (phdr->p_offset + phdr->p_filesz > size) { + pr_err("vm_planes: ELF PT_LOAD file data beyond image\n"); return -EINVAL; + } - p = data + data_align; - } + if (phdr->p_filesz) { + ret = copy_to_early_mem(dest, data + phdr->p_offset, + phdr->p_filesz); + if (ret) + return ret; + } - return -ENOENT; -} + 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; + } -static int __init copy_to_early_mem(phys_addr_t dest, const void *src, - unsigned long size) -{ - unsigned long slop, clen; - char *p; + /* + * 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); + } - while (size) { - slop = offset_in_page(dest); - clen = size; - if (clen > PAGE_SIZE - slop) - clen = PAGE_SIZE - slop; - p = early_memremap(dest & PAGE_MASK, clen + slop); - if (!p) - return -ENOMEM; - memcpy(p + slop, src, clen); - early_memunmap(p, clen + slop); - dest += clen; - src += clen; - size -= clen; + 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; } @@ -525,6 +539,7 @@ static int __init load_plane_kernel_raw(const u8 *data, u32 size, return -ENOMEM; } + cfg->entry_point = cfg->load_offset; return copy_to_early_mem(cfg->load_offset, data, size); } @@ -535,50 +550,59 @@ int __init load_vm_plane_kernels(unsigned int plane_count, int err = 0; for (i = 1; i < plane_count; i++) { - const u8 *data; - u32 size; + void *data; + loff_t fsize; int ret; - ret = find_initrd_file(plane_cfg[i].kernel, &data, &size); + ret = vm_planes_read_file(plane_cfg[i].kernel, &data, &fsize); if (ret) { - pr_err("vm_planes: plane %u: kernel image '%s' not found in initrd\n", - i, plane_cfg[i].kernel); + 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, size, + ret = load_plane_kernel_raw(data, (u32)fsize, &plane_cfg[i]); break; - case VM_PLANE_KFMT_BZIMAGE: case VM_PLANE_KFMT_ELF: - pr_err("vm_planes: plane %u: kernel format not yet supported\n", + 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 image: %d\n", + 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' (%u bytes) at 0x%llx\n", - i, plane_cfg[i].kernel, - size, (unsigned long long)plane_cfg[i].load_offset); + 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; } +/* ---- Early param & activation ---- */ + static int __init parse_enable_vm_planes(char *str) { bool enable; @@ -600,7 +624,8 @@ early_param("enable-vm-planes", parse_enable_vm_planes); 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) { return -ENOSYS; } +int __init __weak activate_vm_planes(unsigned int plane_count, + struct vm_plane_config *plane_cfg) { return -ENOSYS; } void __init arch_init_vm_planes(void) { @@ -614,9 +639,10 @@ void __init arch_init_vm_planes(void) 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); + 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; } @@ -635,7 +661,7 @@ void __init arch_init_vm_planes(void) return; } - ret = activate_vm_planes(plane_count); + ret = activate_vm_planes(plane_count, plane_cfg); if (ret) pr_err("vm_planes: failed to activate planes: %d\n", ret); } -- 2.55.0