@@ -3,8 +3,9 @@
#include <linux/sizes.h>
-#define kvm__arch_get_kern_offset(...) 0x8000
-#define kvm__arch_get_kernel_size(...) 0
+#define kvm__arch_get_kern_offset(...) 0x8000
+#define kvm__arch_get_kernel_size(...) 0
+#define kvm__arch_get_payload_region_size(...) SZ_256M
struct kvm;
static inline void kvm__arch_read_kernel_header(struct kvm *kvm, int fd) {}
@@ -8,6 +8,8 @@ void kvm__arch_read_kernel_header(struct kvm *kvm, int fd);
unsigned long long kvm__arch_get_kern_offset(struct kvm *kvm);
u64 kvm__arch_get_kernel_size(struct kvm *kvm);
+u64 kvm__arch_get_payload_region_size(struct kvm *kvm);
+
int kvm__arch_get_ipa_limit(struct kvm *kvm);
void kvm__arch_enable_mte(struct kvm *kvm);
@@ -135,6 +135,14 @@ u64 kvm__arch_get_kernel_size(struct kvm *kvm)
return le64_to_cpu(kernel_header->image_size);
}
+u64 kvm__arch_get_payload_region_size(struct kvm *kvm)
+{
+ if (kvm->cfg.arch.aarch32_guest)
+ return SZ_256M;
+
+ return SZ_512M;
+}
+
int kvm__arch_get_ipa_limit(struct kvm *kvm)
{
int ret;
@@ -103,14 +103,16 @@ bool kvm__arch_load_kernel_image(struct kvm *kvm, int fd_kernel, int fd_initrd,
{
void *pos, *kernel_end, *limit;
unsigned long guest_addr;
+ u64 payload_region_size;
ssize_t file_size;
u64 kernel_size;
+ payload_region_size = kvm__arch_get_payload_region_size(kvm);
/*
- * Linux requires the initrd and dtb to be mapped inside lowmem,
+ * Linux for arm requires the initrd and dtb to be mapped inside lowmem,
* so we can't just place them at the top of memory.
*/
- limit = kvm->ram_start + min(kvm->ram_size, (u64)SZ_256M);
+ limit = kvm->ram_start + min(kvm->ram_size, payload_region_size);
kvm__arch_read_kernel_header(kvm, fd_kernel);
kvmtool uses the same memory map for 64bit and 32bit guests, where it copies the kernel, the initrd and DTB in the bottom 256MB. The restriction on placing everything in the bottom 256MB comes from the aarch32 boot protocol, where the kernel, DTB and initrd must be placed in a region covered by the low-memory mapping. The size of the lowmem region varies based on the kernel-userspace split, which is a Kconfig option, and on the vmalloc area size, which can be specified by the user on the kernel command line. Hence kvmtool's choice of using the bottom 256MB as a reasonable compromise which has worked well so far. Sina has reported in private that they were unable to create a 64bit virtual machine with a 351MB initrd, and that's due to the 256MB restriction placed on the arm64 payload layout. This restriction is not found in the arm64 boot protocol: booting.rst in the Linux v6.12 source tree specifies that the kernel and initrd must be placed in the same 32GB window. There is also a mention of kernels prior to v4.2 requiring the DTB to be placed within a 512MB region starting at the kernel image minus the kernel offset. Increase the payload region size to 512MB for arm64, which will provide maximum compatibility with Linux guests, while allowing for larger initrds or kernel images. This means that the gap between the DTB (or initrd, if present) and the kernel is larger now. For 32 bit guests, the payload region size has been kept unchanged, because it has proven adequate so far. Reported-by: Abdollahi Sina <s.abdollahi22@imperial.ac.uk> Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com> --- arm/aarch32/include/kvm/kvm-arch.h | 5 +++-- arm/aarch64/include/kvm/kvm-arch.h | 2 ++ arm/aarch64/kvm.c | 8 ++++++++ arm/kvm.c | 6 ++++-- 4 files changed, 17 insertions(+), 4 deletions(-)