Hi bibo mao:
Ok, if the low memory is insufficient, I will just load the initrd from the high memory starting address.On 2025/3/26 上午10:51, lixianglai wrote:It is a little too strict requirement. I think we should assure that it can work at first, and then work better to put it in node0.Hi bibo mao:Xianglai, Thanks for your patch, some comments inline. On 2025/3/19 下午4:32, Xianglai Li wrote:When only the -kernel parameter is used to load the elf kernel, the initrd is loaded in the ram. If the initrd size is too large, the loading fails, resulting in a VM startup failure. This patch first loads initrd near the kernel. When the nearby memory space of the kernel is insufficient, it tries to load it to the starting position of high-end memory. If there is still not enough, qemu will report an error and ask the user to increase the memory space for the virtual machine to boot. Signed-off-by: Xianglai Li <lixiang...@loongson.cn> --- Cc: Bibo Mao <maob...@loongson.cn> Cc: Jiaxun Yang <jiaxun.y...@flygoat.com> Cc: Song Gao <gaos...@loongson.cn> Cc: Xianglai Li <lixiang...@loongson.cn>hw/loongarch/boot.c | 66 ++++++++++++++++++++++++++++++++-----hw/loongarch/virt.c | 1 + include/hw/loongarch/boot.h | 1 + 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 354cf458c8..3f094ebb39 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c@@ -235,6 +235,61 @@ static int64_t load_loongarch_linux_image(const char *filename,return size; }+static void find_initrd_loadoffset(struct loongarch_boot_info *info,+ uint64_t kernel_high, ssize_t kernel_size) +{ + hwaddr base, size, gap; + ram_addr_t initrd_end, initrd_start; + int nb_numa_nodes; + NodeInfo *numa_info; + + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + nb_numa_nodes = info->numa_state->num_nodes; + numa_info = info->numa_state->nodes; + initrd_start = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); + initrd_end = initrd_start + initrd_size; + + if (nb_numa_nodes) { + size = numa_info[0].node_mem;why is memory size of the node0 calculated here? initrd memory can be put at these places: 1) near kernel at low memory region. 2) start from high memory region located at VIRT_HIGHMEM_BASE It seems that it irrelative with numa memoryWhen the initrd cannot be loaded in the low memory, we plan to load the initrd to the starting address of the high memory,so we need to determine whether the high memory has enough space to put the initrd. Meanwhile, we hope that the initrd can be loaded on node0 as much as possibleand avoid crossing memory nodes as much as possible.When qemu specifies the node0 memory size and when no node0 memory size is specified,the calculation method of the node0 memory size is different, so we introduce the calculation of the node0 memory here. If the memory size of node 0 is completely unable to load initrd, we will ask users to increase the memory size of node 0,Just for example, there two 256MB memory in node0(low memory) and node1(high memory), user may think that the total memory size is enough for initrd loading.
Thanks! Xianglai.
Regards Bibo Maoand do not consider loading initrd to other memory nodes, so as to simplify the calculation processno else here. otherwise the sentence as following "size -= gap;" will be negative.+ } else { + size = info->ram_size; + } + /* + * Try to load the initrd near the kernel image + */ + if (size <= gap) { + if (initrd_end <= (base + gap)) { + initrd_offset = initrd_start; + return ; + }Yes,This is a bug, I will fix it!+ } + + /* + * Try to load initrd in the high memory of node0 + */ + size -= gap; + base = VIRT_HIGHMEM_BASE; + initrd_start = ROUND_UP(base, 64 * KiB); + initrd_end = initrd_start + initrd_size; + if (initrd_end <= (base + size)) {it is a little complicated, just the following will be ok if (initrd_size <= size) { initrd_offset = base; return; }Ok!! Thanks! Xianglai.Regards Bibo Mao+ initrd_offset = initrd_start; + return ; + } + + if (nb_numa_nodes == 0) { + error_report("memory too small for initial ram disk '%s'," + "You need to expand the memory space", + info->initrd_filename); + } else { + error_report("memory too small for initial ram disk '%s'," + "You need to expand the memory space of node0", + info->initrd_filename); + + } + exit(1); +} + static int64_t load_kernel_info(struct loongarch_boot_info *info) { uint64_t kernel_entry, kernel_low, kernel_high;@@ -261,16 +316,9 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info)if (info->initrd_filename) { initrd_size = get_image_size(info->initrd_filename); if (initrd_size > 0) {- initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB);- - if (initrd_offset + initrd_size > info->ram_size) {- error_report("memory too small for initial ram disk '%s'",- info->initrd_filename); - exit(1); - } - + find_initrd_loadoffset(info, kernel_high, kernel_size);initrd_size = load_image_targphys(info->initrd_filename, initrd_offset,- info->ram_size - initrd_offset); + initrd_size); } if (initrd_size == (target_ulong)-1) { diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index a5840ff968..eb62abec0e 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -750,6 +750,7 @@ static void virt_init(MachineState *machine) qemu_register_powerdown_notifier(&lvms->powerdown_notifier); lvms->bootinfo.ram_size = ram_size; + lvms->bootinfo.numa_state = machine->numa_state; loongarch_load_kernel(machine, &lvms->bootinfo); }diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.hindex b3b870df1f..e3887d7cc6 100644 --- a/include/hw/loongarch/boot.h +++ b/include/hw/loongarch/boot.h @@ -98,6 +98,7 @@ struct efi_initrd { struct loongarch_boot_info { uint64_t ram_size; + struct NumaState *numa_state; const char *kernel_filename; const char *kernel_cmdline; const char *initrd_filename;