diff options
Diffstat (limited to 'common/image.c')
| -rw-r--r-- | common/image.c | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/common/image.c b/common/image.c new file mode 100644 index 0000000..d114c6a --- /dev/null +++ b/common/image.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * image.c - arm64 kernel Image validation and placement. + * + * The relocation rules are the ones from the kernel boot protocol, + * including the pre a2c1d73b94ed fallback where image_size is 0 and + * the offset was fixed. qemu virt loads us at the bottom of RAM, same + * place the kernel wants to be, so the common case is a no move. + * + * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org> + */ + +#include <string.h> +#include <sys/types.h> +#include <stdint.h> +#include <boot.h> +#include <endian.h> + +#define SZ_2M 0x200000UL +#define SZ_16M 0x1000000UL +#define IMAGE_OFFSET_FIXUP 0x80000ULL + +int tb_image_setup(uintptr_t image, uintptr_t mem_base, uint32 mem_size, + struct tb_image *img) +{ + struct Image_header *ih = (struct Image_header *)image; + uint64_t image_size; + uint64_t text_offset; + uintptr_t dst; + + if (le32_to_cpu(ih->magic) != LINUX_ARM64_IMAGE_MAGIC) + return -1; + + if (le64_to_cpu(ih->image_size) == 0) { + /* ancient image, no size field, assume the old defaults */ + image_size = SZ_16M; + text_offset = IMAGE_OFFSET_FIXUP; + } else { + image_size = le64_to_cpu(ih->image_size); + text_offset = le64_to_cpu(ih->text_offset); + } + + if (image_size > mem_size) + return -1; + + /* + * flag bit 3 says the image can live anywhere, honour where it + * already is. otherwise the base must be 2MB aligned, the + * physical offset from there is text_offset. + */ + if (le64_to_cpu(ih->flags) & (1ULL << 3)) + dst = image - text_offset; + else + dst = mem_base; + + /* the whole image, header included, sits here, and code0 at its + * start is the first instruction executed */ + img->load = ((dst + SZ_2M - 1) & ~(uintptr_t)(SZ_2M - 1)) + text_offset; + img->ep = img->load; + img->size = image_size; + + if (dst + image_size > mem_base + mem_size) + return -1; + + return 0; +} |
