[PATCH V2] hw/loongarch/boot: Adjust the loading position of the initrd

Xianglai Li posted 1 patch 10 months, 2 weeks ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20250326114035.712130-1-lixianglai@loongson.cn
Maintainers: Song Gao <gaosong@loongson.cn>, Bibo Mao <maobibo@loongson.cn>, Jiaxun Yang <jiaxun.yang@flygoat.com>
There is a newer version of this series
hw/loongarch/boot.c | 53 +++++++++++++++++++++++++++++++++++++--------
1 file changed, 44 insertions(+), 9 deletions(-)
[PATCH V2] hw/loongarch/boot: Adjust the loading position of the initrd
Posted by Xianglai Li 10 months, 2 weeks ago
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 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 <lixianglai@loongson.cn>
---
Cc: Bibo Mao <maobibo@loongson.cn>
Cc: Jiaxun Yang <jiaxun.yang@flygoat.com>
Cc: Song Gao <gaosong@loongson.cn>
Cc: Xianglai Li <lixianglai@loongson.cn>

ChangeLog:
V1->V2:
If the low memory is insufficient, the initrd is directly loaded
from the start address of the high memory, and the node0 memory space is not counted.

 hw/loongarch/boot.c | 53 +++++++++++++++++++++++++++++++++++++--------
 1 file changed, 44 insertions(+), 9 deletions(-)

diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c
index 354cf458c8..c24edfeb59 100644
--- a/hw/loongarch/boot.c
+++ b/hw/loongarch/boot.c
@@ -235,6 +235,48 @@ 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, low_end;
+    ram_addr_t initrd_end, initrd_start;
+
+    base = VIRT_LOWMEM_BASE;
+    gap = VIRT_LOWMEM_SIZE;
+    initrd_start = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB);
+    initrd_end = initrd_start + initrd_size;
+
+    size = info->ram_size;
+    low_end = base + MIN(size, gap);
+    if (initrd_end <= low_end) {
+        initrd_offset = initrd_start;
+        return ;
+    }
+
+    if (size <= gap) {
+        error_report("The low memory too small for initial ram disk '%s',"
+             "You need to expand the memory space",
+             info->initrd_filename);
+        exit(1);
+    }
+
+    /*
+     * Try to load initrd in the high memory
+     */
+    size -= gap;
+    base = VIRT_HIGHMEM_BASE;
+    initrd_start = ROUND_UP(base, 64 * KiB);
+    if (initrd_size <= size) {
+        initrd_offset = initrd_start;
+        return ;
+    }
+
+    error_report("The high memory too small for initial ram disk '%s',"
+         "You need to expand the memory space",
+         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 +303,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) {
-- 
2.39.1
Re: [PATCH V2] hw/loongarch/boot: Adjust the loading position of the initrd
Posted by bibo mao 9 months, 2 weeks ago

On 2025/3/26 下午7:40, 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 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.
With the changelog, the maximum characters are 75 in a line. It seems 
that most is 50-65, it is too short :)

Also there should be an empty line for two paragraphs.
> 
> Signed-off-by: Xianglai Li <lixianglai@loongson.cn>
> ---
> Cc: Bibo Mao <maobibo@loongson.cn>
> Cc: Jiaxun Yang <jiaxun.yang@flygoat.com>
> Cc: Song Gao <gaosong@loongson.cn>
> Cc: Xianglai Li <lixianglai@loongson.cn>
> 
> ChangeLog:
> V1->V2:
> If the low memory is insufficient, the initrd is directly loaded
> from the start address of the high memory, and the node0 memory space is not counted.
> 
>   hw/loongarch/boot.c | 53 +++++++++++++++++++++++++++++++++++++--------
>   1 file changed, 44 insertions(+), 9 deletions(-)
> 
> diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c
> index 354cf458c8..c24edfeb59 100644
> --- a/hw/loongarch/boot.c
> +++ b/hw/loongarch/boot.c
> @@ -235,6 +235,48 @@ static int64_t load_loongarch_linux_image(const char *filename,
>       return size;
>   }
>   
> +static void find_initrd_loadoffset(struct loongarch_boot_info *info,
The function find_initrd_loadoffset() is a little strange, how about
get_initrd_memory() or alloc_initrd_memory()?

> +                uint64_t kernel_high, ssize_t kernel_size)
> +{
> +    hwaddr base, size, gap, low_end;
> +    ram_addr_t initrd_end, initrd_start;
> +
> +    base = VIRT_LOWMEM_BASE;
> +    gap = VIRT_LOWMEM_SIZE;
> +    initrd_start = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB);
I think it should move out of this function, it is irrelative with 
initrd memory allocation.
> +    initrd_end = initrd_start + initrd_size;
Had better not use global variable initrd_size, I will submit one patch 
to remove global variables about initrd.
> +
> +    size = info->ram_size;
> +    low_end = base + MIN(size, gap);
> +    if (initrd_end <= low_end) {
> +        initrd_offset = initrd_start;
> +        return ;

> +    }
> +
> +    if (size <= gap) {
> +        error_report("The low memory too small for initial ram disk '%s',"
> +             "You need to expand the memory space",
> +             info->initrd_filename);
> +        exit(1);
> +    }
> +
> +    /*
> +     * Try to load initrd in the high memory
> +     */
> +    size -= gap;
> +    base = VIRT_HIGHMEM_BASE;
> +    initrd_start = ROUND_UP(base, 64 * KiB);
this line is unnecessary, how about
        initrd_start = VIRT_HIGHMEM_BASE;
> +    if (initrd_size <= size) {
> +        initrd_offset = initrd_start;
> +        return ;
> +    }
> +
> +    error_report("The high memory too small for initial ram disk '%s',"
> +         "You need to expand the memory space",
> +         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 +303,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);
> -            }
> -
I think these line can keep unchanged.
> +            find_initrd_loadoffset(info, kernel_high, kernel_size);
how about add return and input parameter for this function.
initrd_offset = alloc_initrd_memory(info, initrd_offset, initrd_size);

Regards
Bibo Mao
>               initrd_size = load_image_targphys(info->initrd_filename, initrd_offset,
> -                                              info->ram_size - initrd_offset);
> +                                              initrd_size);
>           }
>   
>           if (initrd_size == (target_ulong)-1) {
>