[PATCH] powerpc/boot: Fix the initrd being overwritten under qemu
Alexey Kardashevskiy
aik at ozlabs.ru
Wed Oct 23 18:00:21 AEDT 2019
On 23/10/2019 12:36, Oliver O'Halloran wrote:
> When booting under OF the zImage expects the initrd address and size to be
> passed to it using registers r3 and r4. SLOF (guest firmware used by QEMU)
> currently doesn't do this so the zImage is not aware of the initrd
> location. This can result in initrd corruption either though the zImage
> extracting the vmlinux over the initrd, or by the vmlinux overwriting the
> initrd when relocating itself.
>
> QEMU does put the linux,initrd-start and linux,initrd-end properties into
> the devicetree to vmlinux to find the initrd. We can work around the SLOF
> bug by also looking those properties in the zImage.
This does not boot zImage for me anyway:
Trying to unpack rootfs image as initramfs...
rootfs image is not initramfs (invalid magic at start of compressed archive); looks like an initrd
>
> Cc: stable at vger.kernel.org
> Cc: Alexey Kardashevskiy <aik at ozlabs.ru>
> Signed-off-by: Oliver O'Halloran <oohall at gmail.com>
> ---
> First noticed here: https://unix.stackexchange.com/questions/547023/linux-kernel-on-ppc64le-vmlinux-equivalent-in-arch-powerpc-boot
> ---
> arch/powerpc/boot/devtree.c | 21 +++++++++++++++++++++
> arch/powerpc/boot/main.c | 7 +++++++
> arch/powerpc/boot/of.h | 16 ----------------
> arch/powerpc/boot/ops.h | 1 +
> arch/powerpc/boot/swab.h | 17 +++++++++++++++++
> 5 files changed, 46 insertions(+), 16 deletions(-)
>
> diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c
> index 5d91036..ac5c26b 100644
> --- a/arch/powerpc/boot/devtree.c
> +++ b/arch/powerpc/boot/devtree.c
> @@ -13,6 +13,7 @@
> #include "string.h"
> #include "stdio.h"
> #include "ops.h"
> +#include "swab.h"
>
> void dt_fixup_memory(u64 start, u64 size)
> {
> @@ -318,6 +319,26 @@ int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size)
> return dt_xlate(node, res, reglen, addr, size);
> }
>
> +int dt_read_addr(void *node, const char *prop, unsigned long *out_addr)
> +{
> + int reglen;
> +
> + *out_addr = 0;
> +
> + reglen = getprop(node, prop, prop_buf, sizeof(prop_buf)) / 4;
> + if (reglen == 2) {
> + u64 v0 = be32_to_cpu(prop_buf[0]);
> + u64 v1 = be32_to_cpu(prop_buf[1]);
> + *out_addr = (v0 << 32) | v1;
> + } else if (reglen == 1) {
> + *out_addr = be32_to_cpu(prop_buf[0]);
> + } else {
> + return 0;
> + }
> +
> + return 1;
> +}
> +
> int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr)
> {
>
> diff --git a/arch/powerpc/boot/main.c b/arch/powerpc/boot/main.c
> index a9d2091..518af24 100644
> --- a/arch/powerpc/boot/main.c
> +++ b/arch/powerpc/boot/main.c
> @@ -112,6 +112,13 @@ static struct addr_range prep_initrd(struct addr_range vmlinux, void *chosen,
> } else if (initrd_size > 0) {
> printf("Using loader supplied ramdisk at 0x%lx-0x%lx\n\r",
> initrd_addr, initrd_addr + initrd_size);
> + } else if (chosen) {
> + unsigned long initrd_end;
> +
> + dt_read_addr(chosen, "linux,initrd-start", &initrd_addr);
> + dt_read_addr(chosen, "linux,initrd-end", &initrd_end);
> +
> + initrd_size = initrd_end - initrd_addr;
> }
>
> /* If there's no initrd at all, we're done */
> diff --git a/arch/powerpc/boot/of.h b/arch/powerpc/boot/of.h
> index 31b2f5d..dc24770 100644
> --- a/arch/powerpc/boot/of.h
> +++ b/arch/powerpc/boot/of.h
> @@ -26,22 +26,6 @@ typedef u16 __be16;
> typedef u32 __be32;
> typedef u64 __be64;
>
> -#ifdef __LITTLE_ENDIAN__
> -#define cpu_to_be16(x) swab16(x)
> -#define be16_to_cpu(x) swab16(x)
> -#define cpu_to_be32(x) swab32(x)
> -#define be32_to_cpu(x) swab32(x)
> -#define cpu_to_be64(x) swab64(x)
> -#define be64_to_cpu(x) swab64(x)
> -#else
> -#define cpu_to_be16(x) (x)
> -#define be16_to_cpu(x) (x)
> -#define cpu_to_be32(x) (x)
> -#define be32_to_cpu(x) (x)
> -#define cpu_to_be64(x) (x)
> -#define be64_to_cpu(x) (x)
> -#endif
> -
> #define PROM_ERROR (-1u)
>
> #endif /* _PPC_BOOT_OF_H_ */
> diff --git a/arch/powerpc/boot/ops.h b/arch/powerpc/boot/ops.h
> index e060676..5100dd7 100644
> --- a/arch/powerpc/boot/ops.h
> +++ b/arch/powerpc/boot/ops.h
> @@ -95,6 +95,7 @@ void *simple_alloc_init(char *base, unsigned long heap_size,
> extern void flush_cache(void *, unsigned long);
> int dt_xlate_reg(void *node, int res, unsigned long *addr, unsigned long *size);
> int dt_xlate_addr(void *node, u32 *buf, int buflen, unsigned long *xlated_addr);
> +int dt_read_addr(void *node, const char *prop, unsigned long *out);
> int dt_is_compatible(void *node, const char *compat);
> void dt_get_reg_format(void *node, u32 *naddr, u32 *nsize);
> int dt_get_virtual_reg(void *node, void **addr, int nres);
> diff --git a/arch/powerpc/boot/swab.h b/arch/powerpc/boot/swab.h
> index 11d2069..82db2c1 100644
> --- a/arch/powerpc/boot/swab.h
> +++ b/arch/powerpc/boot/swab.h
> @@ -27,4 +27,21 @@ static inline u64 swab64(u64 x)
> (u64)((x & (u64)0x00ff000000000000ULL) >> 40) |
> (u64)((x & (u64)0xff00000000000000ULL) >> 56);
> }
> +
> +#ifdef __LITTLE_ENDIAN__
> +#define cpu_to_be16(x) swab16(x)
> +#define be16_to_cpu(x) swab16(x)
> +#define cpu_to_be32(x) swab32(x)
> +#define be32_to_cpu(x) swab32(x)
> +#define cpu_to_be64(x) swab64(x)
> +#define be64_to_cpu(x) swab64(x)
> +#else
> +#define cpu_to_be16(x) (x)
> +#define be16_to_cpu(x) (x)
> +#define cpu_to_be32(x) (x)
> +#define be32_to_cpu(x) (x)
> +#define cpu_to_be64(x) (x)
> +#define be64_to_cpu(x) (x)
> +#endif
> +
> #endif /* _PPC_BOOT_SWAB_H_ */
>
--
Alexey
More information about the Linuxppc-dev
mailing list