From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from mout.gmx.net ([212.227.15.18]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1fJAty-0003Pj-Up for barebox@lists.infradead.org; Thu, 17 May 2018 04:52:34 +0000 References: <20180516164233.27581-1-linux@rempel-privat.de> <20180516164233.27581-5-linux@rempel-privat.de> <20180516213415.vbeujmqeoavy2bmh@sheep> From: Oleksij Rempel Message-ID: Date: Thu, 17 May 2018 06:52:10 +0200 MIME-Version: 1.0 In-Reply-To: <20180516213415.vbeujmqeoavy2bmh@sheep> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: multipart/mixed; boundary="===============5417794259126288771==" Sender: "barebox" Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: Re: [PATCH v1 04/10] bootm: add kexec ELF support To: Peter Mamonov Cc: barebox@lists.infradead.org This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --===============5417794259126288771== Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="M0gnimUJYoDKDiq8pdZDjg48PRRkEhqKd" This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --M0gnimUJYoDKDiq8pdZDjg48PRRkEhqKd Content-Type: multipart/mixed; boundary="8jEOA3tUZCJieBJNr0yIyWsB9fBl7930B"; protected-headers="v1" From: Oleksij Rempel To: Peter Mamonov Cc: barebox@lists.infradead.org Message-ID: Subject: Re: [PATCH v1 04/10] bootm: add kexec ELF support References: <20180516164233.27581-1-linux@rempel-privat.de> <20180516164233.27581-5-linux@rempel-privat.de> <20180516213415.vbeujmqeoavy2bmh@sheep> In-Reply-To: <20180516213415.vbeujmqeoavy2bmh@sheep> --8jEOA3tUZCJieBJNr0yIyWsB9fBl7930B Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: quoted-printable Am 16.05.2018 um 23:34 schrieb Peter Mamonov: > Hi! >=20 > On Wed, May 16, 2018 at 06:42:27PM +0200, Oleksij Rempel wrote: >> From: Antony Pavlov >> >> Also introduce reboot() for starting already loaded >> via kexec ELF segments. >> >> Signed-off-by: Antony Pavlov >> --- >> commands/Kconfig | 7 + >> common/Kconfig | 3 + >> include/bootm.h | 3 + >> include/kexec.h | 118 +++++++++ >> lib/Makefile | 1 + >> lib/kexec/Makefile | 4 + >> lib/kexec/kexec-bootm-elf.c | 42 ++++ >> lib/kexec/kexec-elf-exec.c | 70 ++++++ >> lib/kexec/kexec-elf.c | 565 +++++++++++++++++++++++++++++++++++= +++++++++ >> lib/kexec/kexec.c | 255 ++++++++++++++++++++ >> 10 files changed, 1068 insertions(+) >> create mode 100644 include/kexec.h >> create mode 100644 lib/kexec/Makefile >> create mode 100644 lib/kexec/kexec-bootm-elf.c >> create mode 100644 lib/kexec/kexec-elf-exec.c >> create mode 100644 lib/kexec/kexec-elf.c >> create mode 100644 lib/kexec/kexec.c >> >> diff --git a/commands/Kconfig b/commands/Kconfig >> index 951a86963..a49928ad1 100644 >> --- a/commands/Kconfig >> +++ b/commands/Kconfig >> @@ -476,6 +476,13 @@ config CMD_SAVES >> =20 >> Save S-Record file to serial line with offset OFFS and length LEN.= >> =20 >> +config KEXEC >> + bool >> + prompt "bootm ELF image support" >> + depends on CMD_BOOTM && HAS_KEXEC >> + help >> + Support using ELF Images. >> + >> config CMD_UIMAGE >> select UIMAGE >> tristate >> diff --git a/common/Kconfig b/common/Kconfig >> index b7000c4d7..d40c79dbc 100644 >> --- a/common/Kconfig >> +++ b/common/Kconfig >> @@ -1218,3 +1218,6 @@ config HAS_DEBUG_LL >> config DDR_SPD >> bool >> select CRC16 >> + >> +config HAS_KEXEC >> + bool >> diff --git a/include/bootm.h b/include/bootm.h >> index 62951d605..3a642fe70 100644 >> --- a/include/bootm.h >> +++ b/include/bootm.h >> @@ -72,6 +72,9 @@ struct image_data { >> =20 >> char *oftree_file; >> char *oftree_part; >> + unsigned long oftree_address; >> + >> + unsigned long cmdline_address; >> =20 >> const void *fit_kernel; >> unsigned long fit_kernel_size; >> diff --git a/include/kexec.h b/include/kexec.h >> new file mode 100644 >> index 000000000..675658b38 >> --- /dev/null >> +++ b/include/kexec.h >> @@ -0,0 +1,118 @@ >> +#ifndef _LINUX_REBOOT_H >> +#define _LINUX_REBOOT_H >> + >> +#include >> + >> +struct kexec_segment { >> + const void *buf; >> + size_t bufsz; >> + const void *mem; >> + size_t memsz; >> +}; >> + >> +struct kexec_info { >> + struct kexec_segment *segment; >> + int nr_segments; >> + void *entry; >> + unsigned long kexec_flags; >> +}; >> + >> +typedef int (probe_t)(const char *kernel_buf, off_t kernel_size); >> +typedef int (load_t)(const char *kernel_buf, off_t kernel_size, >> + struct kexec_info *info); >> +struct kexec_file_type { >> + const char *name; >> + probe_t *probe; >> + load_t *load; >> +}; >> + >> +extern struct kexec_file_type kexec_file_type[]; >> +extern int kexec_file_types; >> + >> +extern void add_segment(struct kexec_info *info, >> + const void *buf, size_t bufsz, unsigned long base, size_t memsz); >> +extern void add_segment_phys_virt(struct kexec_info *info, >> + const void *buf, size_t bufsz, unsigned long base, size_t memsz, >> + int phys); >> + >> +extern int kexec_load(struct image_data *data, void *entry, >> + unsigned long nr_segments, >> + struct kexec_segment *segments); >> + >> +extern void kexec_arch(void *opaque); >> + >> +extern int kexec_load_bootm_data(struct image_data *data); >> + >> +/* These values match the ELF architecture values. >> + * Unless there is a good reason that should continue to be the case.= >> + */ >> +#define KEXEC_ARCH_DEFAULT (0 << 16) >> +#define KEXEC_ARCH_386 (3 << 16) >> +#define KEXEC_ARCH_X86_64 (62 << 16) >> +#define KEXEC_ARCH_PPC (20 << 16) >> +#define KEXEC_ARCH_PPC64 (21 << 16) >> +#define KEXEC_ARCH_IA_64 (50 << 16) >> +#define KEXEC_ARCH_ARM (40 << 16) >> +#define KEXEC_ARCH_S390 (22 << 16) >> +#define KEXEC_ARCH_SH (42 << 16) >> +#define KEXEC_ARCH_MIPS_LE (10 << 16) >> +#define KEXEC_ARCH_MIPS (8 << 16) >> +#define KEXEC_ARCH_CRIS (76 << 16) >> + >> +#define KEXEC_MAX_SEGMENTS 16 >> + >> +struct mem_phdr { >> + u64 p_paddr; >> + u64 p_vaddr; >> + u64 p_filesz; >> + u64 p_memsz; >> + u64 p_offset; >> + const char *p_data; >> + u32 p_type; >> + u32 p_flags; >> + u64 p_align; >> +}; >> + >> +struct mem_shdr { >> + u32 sh_name; >> + u32 sh_type; >> + u64 sh_flags; >> + u64 sh_addr; >> + u64 sh_offset; >> + u64 sh_size; >> + u32 sh_link; >> + u32 sh_info; >> + u64 sh_addralign; >> + u64 sh_entsize; >> + const unsigned char *sh_data; >> +}; >> + >> +struct mem_ehdr { >> + u32 ei_class; >> + u32 ei_data; >> + u32 e_type; >> + u32 e_machine; >> + u32 e_version; >> + u32 e_flags; >> + u32 e_phnum; >> + u32 e_shnum; >> + u32 e_shstrndx; >> + u64 e_entry; >> + u64 e_phoff; >> + u64 e_shoff; >> + struct mem_phdr *e_phdr; >> + struct mem_shdr *e_shdr; >> +}; >> + >> +void free_elf_info(struct mem_ehdr *ehdr); >> +int build_elf_info(const char *buf, size_t len, struct mem_ehdr *ehdr= ); >> +int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *= ehdr); >> + >> +int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info); >> + >> +unsigned long elf_max_addr(const struct mem_ehdr *ehdr); >> +int check_room_for_elf(struct list_head *elf_segments); >> +resource_size_t dcheck_res(struct list_head *elf_segments); >> +void list_add_used_region(struct list_head *new, struct list_head *he= ad); >> + >> +#endif /* _LINUX_REBOOT_H */ >> diff --git a/lib/Makefile b/lib/Makefile >> index a7498288a..eebaf3488 100644 >> --- a/lib/Makefile >> +++ b/lib/Makefile >> @@ -65,3 +65,4 @@ obj-y +=3D int_sqrt.o >> obj-y +=3D parseopt.o >> obj-y +=3D clz_ctz.o >> obj-$(CONFIG_CRC_CCITT) +=3D crc-ccitt.o >> +obj-$(CONFIG_KEXEC) +=3D kexec/ >> diff --git a/lib/kexec/Makefile b/lib/kexec/Makefile >> new file mode 100644 >> index 000000000..2f3dc1dd3 >> --- /dev/null >> +++ b/lib/kexec/Makefile >> @@ -0,0 +1,4 @@ >> +obj-y +=3D kexec.o >> +obj-y +=3D kexec-elf.o >> +obj-y +=3D kexec-elf-exec.o >> +obj-y +=3D kexec-bootm-elf.o >> diff --git a/lib/kexec/kexec-bootm-elf.c b/lib/kexec/kexec-bootm-elf.c= >> new file mode 100644 >> index 000000000..2530466a5 >> --- /dev/null >> +++ b/lib/kexec/kexec-bootm-elf.c >> @@ -0,0 +1,42 @@ >> +// SPDX-License-Identifier: GPL-2.0+ >> + >> +#include >> +#include >> +#include >> +#include >> + >> +static int do_bootm_elf(struct image_data *data) >> +{ >> + int ret; >> + >> + ret =3D kexec_load_bootm_data(data); >> + if (IS_ERR_VALUE(ret)) >> + return ret; >> +=09 >> + kexec_arch(data); >> + >> + return -ERESTARTSYS; >> +} >> + >> +static struct image_handler elf_handler =3D { >> + .name =3D "ELF", >> + .bootm =3D do_bootm_elf, >> + .filetype =3D filetype_elf, >> +}; >> + >> +static struct binfmt_hook binfmt_elf_hook =3D { >> + .type =3D filetype_elf, >> + .exec =3D "bootm", >> +}; >> + >> +static int elf_register_image_handler(void) >> +{ >> + int ret; >> + >> + ret =3D register_image_handler(&elf_handler); >> + if (IS_ERR_VALUE(ret)) >> + return ret; >> +=09 >> + return binfmt_register(&binfmt_elf_hook); >> +} >> +late_initcall(elf_register_image_handler); >> diff --git a/lib/kexec/kexec-elf-exec.c b/lib/kexec/kexec-elf-exec.c >> new file mode 100644 >> index 000000000..e910c4ea0 >> --- /dev/null >> +++ b/lib/kexec/kexec-elf-exec.c >> @@ -0,0 +1,70 @@ >> +// SPDX-License-Identifier: GPL-2.0+ >> + >> +#include >> +#include >> + >> +int build_elf_exec_info(const char *buf, off_t len, struct mem_ehdr *= ehdr) >> +{ >> + struct mem_phdr *phdr, *end_phdr; >> + int ret; >> + >> + ret =3D build_elf_info(buf, len, ehdr); >> + if (IS_ERR_VALUE(ret)) >> + return ret; >> + >> + if (ehdr->e_type !=3D ET_EXEC) { >> + pr_err("Not ELF type ET_EXEC\n"); >> + return -ENOEXEC; >> + } >> + >> + if (!ehdr->e_phdr) { >> + pr_err("No ELF program header\n"); >> + return -ENOEXEC; >> + } >> + >> + end_phdr =3D &ehdr->e_phdr[ehdr->e_phnum]; >> + for (phdr =3D ehdr->e_phdr; phdr !=3D end_phdr; phdr++) { >> + /* Kexec does not support loading interpreters. >> + * In addition this check keeps us from attempting >> + * to kexec ordinay executables. >> + */ >> + if (phdr->p_type =3D=3D PT_INTERP) { >> + pr_err("Requires an ELF interpreter\n"); >> + return -ENOEXEC; >> + } >> + } >> + >> + return 0; >> +} >> + >> +int elf_exec_load(struct mem_ehdr *ehdr, struct kexec_info *info) >> +{ >> + size_t i; >> + >> + if (!ehdr->e_phdr) { >> + pr_err("No program header?\n"); >> + return -ENOENT; >> + } >> + >> + /* Read in the PT_LOAD segments */ >> + for (i =3D 0; i < ehdr->e_phnum; i++) { >> + struct mem_phdr *phdr; >> + size_t size; >> + >> + phdr =3D &ehdr->e_phdr[i]; >> + >> + if (phdr->p_type !=3D PT_LOAD) >> + continue; >> + >> + size =3D phdr->p_filesz; >> + >> + if (size > phdr->p_memsz) >> + size =3D phdr->p_memsz; >> + >> + add_segment(info, >> + phdr->p_data, size, >> + phdr->p_paddr, phdr->p_memsz); >> + } >> + >> + return 0; >> +} >> diff --git a/lib/kexec/kexec-elf.c b/lib/kexec/kexec-elf.c >> new file mode 100644 >> index 000000000..a66ac4c66 >> --- /dev/null >> +++ b/lib/kexec/kexec-elf.c >> @@ -0,0 +1,565 @@ >> +// SPDX-License-Identifier: GPL-2.0+ >> + >> +#include >> +#include >> +#include >> +#include >> + >> +static u16 elf16_to_cpu(const struct mem_ehdr *ehdr, u16 val) >> +{ >> + return ehdr->ei_data =3D=3D ELFDATA2LSB ? le16_to_cpu(val) >> + : be16_to_cpu(val); >> +} >> + >> +static u32 elf32_to_cpu(const struct mem_ehdr *ehdr, u32 val) >> +{ >> + return ehdr->ei_data =3D=3D ELFDATA2LSB ? le32_to_cpu(val) >> + : be32_to_cpu(val); >> +} >> + >> +static u64 elf64_to_cpu(const struct mem_ehdr *ehdr, u64 val) >> +{ >> + return ehdr->ei_data =3D=3D ELFDATA2LSB ? le64_to_cpu(val) >> + : be64_to_cpu(val); >> +} >> + >> +static int build_mem_elf32_ehdr(const void *buf, size_t len, >> + struct mem_ehdr *ehdr) >> +{ >> + const Elf32_Ehdr *lehdr =3D buf; >> + >> + if (len < sizeof(Elf32_Ehdr)) { >> + pr_err("Buffer is too small to hold ELF header\n"); >> + return -ENOEXEC; >> + } >> + >> + if (elf16_to_cpu(ehdr, lehdr->e_ehsize) !=3D sizeof(Elf32_Ehdr)) { >> + pr_err("Bad ELF header size\n"); >> + return -ENOEXEC; >> + } >> + >> + ehdr->e_type =3D elf16_to_cpu(ehdr, lehdr->e_type); >> + ehdr->e_machine =3D elf16_to_cpu(ehdr, lehdr->e_machine); >> + ehdr->e_version =3D elf32_to_cpu(ehdr, lehdr->e_version); >> + ehdr->e_entry =3D elf32_to_cpu(ehdr, lehdr->e_entry); >> + ehdr->e_phoff =3D elf32_to_cpu(ehdr, lehdr->e_phoff); >> + ehdr->e_shoff =3D elf32_to_cpu(ehdr, lehdr->e_shoff); >> + ehdr->e_flags =3D elf32_to_cpu(ehdr, lehdr->e_flags); >> + ehdr->e_phnum =3D elf16_to_cpu(ehdr, lehdr->e_phnum); >> + ehdr->e_shnum =3D elf16_to_cpu(ehdr, lehdr->e_shnum); >> + ehdr->e_shstrndx =3D elf16_to_cpu(ehdr, lehdr->e_shstrndx); >> + >> + if ((ehdr->e_phnum > 0) && >> + (elf16_to_cpu(ehdr, lehdr->e_phentsize) !=3D sizeof(Elf32_Phdr))) {= >> + pr_err("ELF bad program header size\n"); >> + return -ENOEXEC; >> + } >> + >> + if ((ehdr->e_shnum > 0) && >> + (elf16_to_cpu(ehdr, lehdr->e_shentsize) !=3D sizeof(Elf32_Shdr))) {= >> + pr_err("ELF bad section header size\n"); >> + return -ENOEXEC; >> + } >> + >> + return 0; >> +} >> + >> +static int build_mem_elf64_ehdr(const void *buf, size_t len, >> + struct mem_ehdr *ehdr) >> +{ >> + const Elf64_Ehdr *lehdr =3D buf; >> + >> + if (len < sizeof(Elf64_Ehdr)) { >> + pr_err("Buffer is too small to hold ELF header\n"); >> + return -ENOEXEC; >> + } >> + >> + if (elf16_to_cpu(ehdr, lehdr->e_ehsize) !=3D sizeof(Elf64_Ehdr)) { >> + pr_err("Bad ELF header size\n"); >> + return -ENOEXEC; >> + } >> + >> + ehdr->e_type =3D elf16_to_cpu(ehdr, lehdr->e_type); >> + ehdr->e_machine =3D elf16_to_cpu(ehdr, lehdr->e_machine); >> + ehdr->e_version =3D elf32_to_cpu(ehdr, lehdr->e_version); >> + ehdr->e_entry =3D elf64_to_cpu(ehdr, lehdr->e_entry); >> + ehdr->e_phoff =3D elf64_to_cpu(ehdr, lehdr->e_phoff); >> + ehdr->e_shoff =3D elf64_to_cpu(ehdr, lehdr->e_shoff); >> + ehdr->e_flags =3D elf32_to_cpu(ehdr, lehdr->e_flags); >> + ehdr->e_phnum =3D elf16_to_cpu(ehdr, lehdr->e_phnum); >> + ehdr->e_shnum =3D elf16_to_cpu(ehdr, lehdr->e_shnum); >> + ehdr->e_shstrndx =3D elf16_to_cpu(ehdr, lehdr->e_shstrndx); >> + >> + if ((ehdr->e_phnum > 0) && >> + (elf16_to_cpu(ehdr, lehdr->e_phentsize) !=3D sizeof(Elf64_Phdr))) {= >> + pr_err("ELF bad program header size\n"); >> + return -ENOEXEC; >> + } >> + >> + if ((ehdr->e_shnum > 0) && >> + (elf16_to_cpu(ehdr, lehdr->e_shentsize) !=3D sizeof(Elf64_Shdr))) {= >> + pr_err("ELF bad section header size\n"); >> + return -ENOEXEC; >> + } >> + >> + return 0; >> +} >> + >> +static int build_mem_ehdr(const void *buf, size_t len, struct mem_ehd= r *ehdr) >> +{ >> + unsigned char e_ident[EI_NIDENT]; >> + int ret; >> + >> + memset(ehdr, 0, sizeof(*ehdr)); >> + >> + if (len < sizeof(e_ident)) { >> + pr_err("Buffer is too small to hold ELF e_ident\n"); >> + return -ENOEXEC; >> + } >> + >> + memcpy(e_ident, buf, sizeof(e_ident)); >> + >> + ehdr->ei_class =3D e_ident[EI_CLASS]; >> + ehdr->ei_data =3D e_ident[EI_DATA]; >> + if ((ehdr->ei_class !=3D ELFCLASS32) && >> + (ehdr->ei_class !=3D ELFCLASS64)) { >> + pr_err("Not a supported ELF class\n"); >> + return -ENOEXEC; >> + } >> + >> + if ((ehdr->ei_data !=3D ELFDATA2LSB) && >> + (ehdr->ei_data !=3D ELFDATA2MSB)) { >> + pr_err("Not a supported ELF data format\n"); >> + return -ENOEXEC; >> + } >> + >> + if (ehdr->ei_class =3D=3D ELFCLASS32) >> + ret =3D build_mem_elf32_ehdr(buf, len, ehdr); >> + else >> + ret =3D build_mem_elf64_ehdr(buf, len, ehdr); >> + >> + if (IS_ERR_VALUE(ret)) >> + return ret; >> + >> + if ((e_ident[EI_VERSION] !=3D EV_CURRENT) || >> + (ehdr->e_version !=3D EV_CURRENT)) { >> + pr_err("Unknown ELF version\n"); >> + return -ENOEXEC; >> + } >> + >> + return 0; >> +} >> + >> +static void build_mem_elf32_phdr(const char *buf, struct mem_ehdr *eh= dr, int idx) >> +{ >> + struct mem_phdr *phdr; >> + const Elf32_Phdr *lphdr; >> + >> + lphdr =3D (const Elf32_Phdr *)(buf + ehdr->e_phoff + (idx * sizeof(E= lf32_Phdr))); >> + phdr =3D &ehdr->e_phdr[idx]; >> + >> + phdr->p_type =3D elf32_to_cpu(ehdr, lphdr->p_type); >> + phdr->p_paddr =3D elf32_to_cpu(ehdr, lphdr->p_paddr); >> + phdr->p_vaddr =3D elf32_to_cpu(ehdr, lphdr->p_vaddr); >> + phdr->p_filesz =3D elf32_to_cpu(ehdr, lphdr->p_filesz); >> + phdr->p_memsz =3D elf32_to_cpu(ehdr, lphdr->p_memsz); >> + phdr->p_offset =3D elf32_to_cpu(ehdr, lphdr->p_offset); >> + phdr->p_flags =3D elf32_to_cpu(ehdr, lphdr->p_flags); >> + phdr->p_align =3D elf32_to_cpu(ehdr, lphdr->p_align); >> +} >> + >> +static void build_mem_elf64_phdr(const char *buf, struct mem_ehdr *eh= dr, int idx) >> +{ >> + struct mem_phdr *phdr; >> + const Elf64_Phdr *lphdr; >> + >> + lphdr =3D (const Elf64_Phdr *)(buf + ehdr->e_phoff + (idx * sizeof(E= lf64_Phdr))); >> + phdr =3D &ehdr->e_phdr[idx]; >> + >> + phdr->p_type =3D elf32_to_cpu(ehdr, lphdr->p_type); >> + phdr->p_paddr =3D elf64_to_cpu(ehdr, lphdr->p_paddr); >> + phdr->p_vaddr =3D elf64_to_cpu(ehdr, lphdr->p_vaddr); >> + phdr->p_filesz =3D elf64_to_cpu(ehdr, lphdr->p_filesz); >> + phdr->p_memsz =3D elf64_to_cpu(ehdr, lphdr->p_memsz); >> + phdr->p_offset =3D elf64_to_cpu(ehdr, lphdr->p_offset); >> + phdr->p_flags =3D elf32_to_cpu(ehdr, lphdr->p_flags); >> + phdr->p_align =3D elf64_to_cpu(ehdr, lphdr->p_align); >> +} >> + >> +static int build_mem_phdrs(const char *buf, struct mem_ehdr *ehdr) >> +{ >> + size_t mem_phdr_size, i; >> + >> + /* e_phnum is at most 65535 so calculating >> + * the size of the program header cannot overflow. >> + */ >> + >> + /* Allocate the e_phdr array */ >> + mem_phdr_size =3D sizeof(ehdr->e_phdr[0]) * ehdr->e_phnum; >> + ehdr->e_phdr =3D xmalloc(mem_phdr_size); >> + >> + for (i =3D 0; i < ehdr->e_phnum; i++) { >> + struct mem_phdr *phdr; >> + >> + if (ehdr->ei_class =3D=3D ELFCLASS32) >> + build_mem_elf32_phdr(buf, ehdr, i); >> + else >> + build_mem_elf64_phdr(buf, ehdr, i); >> + >> + /* Check the program headers to be certain >> + * they are safe to use. >> + */ >> + phdr =3D &ehdr->e_phdr[i]; >> + if ((phdr->p_paddr + phdr->p_memsz) < phdr->p_paddr) { >> + /* The memory address wraps */ >> + pr_err("ELF address wrap around\n"); >> + return -ENOEXEC; >> + } >> + >> + /* Remember where the segment lives in the buffer */ >> + phdr->p_data =3D buf + phdr->p_offset; >> + } >> + >> + return 0; >> +} >> + >> +static int build_mem_elf32_shdr(const char *buf, struct mem_ehdr *ehd= r, int idx) >> +{ >> + struct mem_shdr *shdr; >> + int size_ok; >> + const Elf32_Shdr *lshdr; >> + >> + lshdr =3D (const Elf32_Shdr *)(buf + ehdr->e_shoff + (idx * sizeof(E= lf32_Shdr))); >> + shdr =3D &ehdr->e_shdr[idx]; >> + shdr->sh_name =3D elf32_to_cpu(ehdr, lshdr->sh_name); >> + shdr->sh_type =3D elf32_to_cpu(ehdr, lshdr->sh_type); >> + shdr->sh_flags =3D elf32_to_cpu(ehdr, lshdr->sh_flags); >> + shdr->sh_addr =3D elf32_to_cpu(ehdr, lshdr->sh_addr); >> + shdr->sh_offset =3D elf32_to_cpu(ehdr, lshdr->sh_offset); >> + shdr->sh_size =3D elf32_to_cpu(ehdr, lshdr->sh_size); >> + shdr->sh_link =3D elf32_to_cpu(ehdr, lshdr->sh_link); >> + shdr->sh_info =3D elf32_to_cpu(ehdr, lshdr->sh_info); >> + shdr->sh_addralign =3D elf32_to_cpu(ehdr, lshdr->sh_addralign); >> + shdr->sh_entsize =3D elf32_to_cpu(ehdr, lshdr->sh_entsize); >> + >> + /* Now verify sh_entsize */ >> + size_ok =3D 0; >> + switch (shdr->sh_type) { >> + case SHT_SYMTAB: >> + size_ok =3D shdr->sh_entsize =3D=3D sizeof(Elf32_Sym); >> + break; >> + case SHT_RELA: >> + size_ok =3D shdr->sh_entsize =3D=3D sizeof(Elf32_Rela); >> + break; >> + case SHT_DYNAMIC: >> + size_ok =3D shdr->sh_entsize =3D=3D sizeof(Elf32_Dyn); >> + break; >> + case SHT_REL: >> + size_ok =3D shdr->sh_entsize =3D=3D sizeof(Elf32_Rel); >> + break; >> + case SHT_NOTE: >> + case SHT_NULL: >> + case SHT_PROGBITS: >> + case SHT_HASH: >> + case SHT_NOBITS: >> + default: >> + /* This is a section whose entsize requirements >> + * I don't care about. If I don't know about >> + * the section I can't care about it's entsize >> + * requirements. >> + */ >> + size_ok =3D 1; >> + break; >> + } >> + >> + if (size_ok) >> + return 0; >> + >> + pr_err("Bad section header(%x) entsize: %lld\n", >> + shdr->sh_type, shdr->sh_entsize); >> + return -ENOEXEC; >> +} >> + >> +static int build_mem_elf64_shdr(const char *buf, struct mem_ehdr *ehd= r, int idx) >> +{ >> + struct mem_shdr *shdr; >> + int size_ok; >> + const Elf64_Shdr *lshdr; >> + >> + lshdr =3D (const Elf64_Shdr *)(buf + ehdr->e_shoff + (idx * sizeof(E= lf64_Shdr))); >> + shdr =3D &ehdr->e_shdr[idx]; >> + shdr->sh_name =3D elf32_to_cpu(ehdr, lshdr->sh_name); >> + shdr->sh_type =3D elf32_to_cpu(ehdr, lshdr->sh_type); >> + shdr->sh_flags =3D elf64_to_cpu(ehdr, lshdr->sh_flags); >> + shdr->sh_addr =3D elf64_to_cpu(ehdr, lshdr->sh_addr); >> + shdr->sh_offset =3D elf64_to_cpu(ehdr, lshdr->sh_offset); >> + shdr->sh_size =3D elf64_to_cpu(ehdr, lshdr->sh_size); >> + shdr->sh_link =3D elf32_to_cpu(ehdr, lshdr->sh_link); >> + shdr->sh_info =3D elf32_to_cpu(ehdr, lshdr->sh_info); >> + shdr->sh_addralign =3D elf64_to_cpu(ehdr, lshdr->sh_addralign); >> + shdr->sh_entsize =3D elf64_to_cpu(ehdr, lshdr->sh_entsize); >> + >> + /* Now verify sh_entsize */ >> + size_ok =3D 0; >> + switch (shdr->sh_type) { >> + case SHT_SYMTAB: >> + size_ok =3D shdr->sh_entsize =3D=3D sizeof(Elf64_Sym); >> + break; >> + case SHT_RELA: >> + size_ok =3D shdr->sh_entsize =3D=3D sizeof(Elf64_Rela); >> + break; >> + case SHT_DYNAMIC: >> + size_ok =3D shdr->sh_entsize =3D=3D sizeof(Elf64_Dyn); >> + break; >> + case SHT_REL: >> + size_ok =3D shdr->sh_entsize =3D=3D sizeof(Elf64_Rel); >> + break; >> + case SHT_NOTE: >> + case SHT_NULL: >> + case SHT_PROGBITS: >> + case SHT_HASH: >> + case SHT_NOBITS: >> + default: >> + /* This is a section whose entsize requirements >> + * I don't care about. If I don't know about >> + * the section I can't care about it's entsize >> + * requirements. >> + */ >> + size_ok =3D 1; >> + break; >> + } >> + >> + if (size_ok) >> + return 0; >> + >> + pr_err("Bad section header(%x) entsize: %lld\n", >> + shdr->sh_type, shdr->sh_entsize); >> + return -ENOEXEC; >> +} >> + >> +static int build_mem_shdrs(const void *buf, struct mem_ehdr *ehdr) >> +{ >> + size_t mem_shdr_size, i; >> + >> + /* Allocate the e_shdr array */ >> + mem_shdr_size =3D sizeof(ehdr->e_shdr[0]) * ehdr->e_shnum; >> + ehdr->e_shdr =3D xmalloc(mem_shdr_size); >> + >> + for (i =3D 0; i < ehdr->e_shnum; i++) { >> + struct mem_shdr *shdr; >> + int ret; >> + >> + if (ehdr->ei_class =3D=3D ELFCLASS32) >> + ret =3D build_mem_elf32_shdr(buf, ehdr, i); >> + else >> + ret =3D build_mem_elf64_shdr(buf, ehdr, i); >> + >> + if (IS_ERR_VALUE(ret)) >> + return ret; >> + >> + /* Check the section headers to be certain >> + * they are safe to use. >> + */ >> + shdr =3D &ehdr->e_shdr[i]; >> + if ((shdr->sh_addr + shdr->sh_size) < shdr->sh_addr) { >> + pr_err("ELF address wrap around\n"); >> + return -ENOEXEC; >> + } >> + >> + /* Remember where the section lives in the buffer */ >> + shdr->sh_data =3D (unsigned char *)(buf + shdr->sh_offset); >> + } >> + >> + return 0; >> +} >> + >> +void free_elf_info(struct mem_ehdr *ehdr) >> +{ >> + free(ehdr->e_phdr); >> + free(ehdr->e_shdr); >> + memset(ehdr, 0, sizeof(*ehdr)); >> +} >> + >> +int build_elf_info(const char *buf, size_t len, struct mem_ehdr *ehdr= ) >> +{ >> + int ret; >> + >> + ret =3D build_mem_ehdr(buf, len, ehdr); >> + if (IS_ERR_VALUE(ret)) >> + return ret; >> + >> + if ((ehdr->e_phoff > 0) && (ehdr->e_phnum > 0)) { >> + ret =3D build_mem_phdrs(buf, ehdr); >> + if (IS_ERR_VALUE(ret)) { >> + free_elf_info(ehdr); >> + return ret; >> + } >> + } >> + >> + if ((ehdr->e_shoff > 0) && (ehdr->e_shnum > 0)) { >> + ret =3D build_mem_shdrs(buf, ehdr); >> + if (IS_ERR_VALUE(ret)) { >> + free_elf_info(ehdr); >> + return ret; >> + } >> + } >> + >> + return 0; >> +} >> + >> +int check_room_for_elf(struct list_head *elf_segments) >> +{ >> + struct memory_bank *bank; >> + struct resource *res, *r; >> + >> + list_for_each_entry(r, elf_segments, sibling) { >> + int got_bank; >> + >> + got_bank =3D 0; >> + for_each_memory_bank(bank) { >> + unsigned long start, end; >> + >> + res =3D bank->res; >> + >> + start =3D virt_to_phys((const void *)res->start); >> + end =3D virt_to_phys((const void *)res->end); >> + >> + if ((start <=3D r->start) && (end >=3D r->end)) { >> + got_bank =3D 1; >> + break; >> + } >> + } >> + >> + if (!got_bank) >> + return -ENOSPC; >> + } >> + >> + return 0; >> +} >> + >> +/* sort by size */ >> +static int compare(struct list_head *a, struct list_head *b) >> +{ >> + struct resource *ra =3D (struct resource *)list_entry(a, >> + struct resource, sibling); >> + struct resource *rb =3D (struct resource *)list_entry(b, >> + struct resource, sibling); >> + resource_size_t sa, sb; >> + >> + sa =3D ra->end - ra->start; >> + sb =3D rb->end - rb->start; >> + >> + if (sa > sb) >> + return -1; >> + if (sa < sb) >> + return 1; >> + return 0; >> +} >> + >> +void list_add_used_region(struct list_head *new, struct list_head *he= ad) >> +{ >> + struct list_head *pos, *insert =3D head; >> + struct resource *rb =3D >> + (struct resource *)list_entry(new, struct resource, sibling); >> + struct list_head *n; >> + >> + /* rb --- new region */ >> + list_for_each_safe(pos, n, head) { >> + struct resource *ra =3D (struct resource *)list_entry(pos, >> + struct resource, sibling); >> + >> + if (resource_overlaps(ra, rb)) { >> + rb->start =3D min(ra->start, rb->start); >> + rb->end =3D max(ra->end, rb->end); >> + rb->name =3D "join"; >> + list_del(pos); >> + } >> + } >> + >> + list_for_each(pos, head) { >> + struct resource *ra =3D (struct resource *)list_entry(pos, >> + struct resource, sibling); >> + >> + if (ra->start < rb->start) >> + continue; >> + >> + insert =3D pos; >> + break; >> + } >> + >> + list_add_tail(new, insert); >> +} >> + >> +resource_size_t dcheck_res(struct list_head *elf_segments) >> +{ >> + struct memory_bank *bank; >> + struct resource *res, *r, *t; >> + >> + LIST_HEAD(elf_relocate_banks); >> + LIST_HEAD(elf_relocate_banks_size_sorted); >> + LIST_HEAD(used_regions); >> + >> + for_each_memory_bank(bank) { >> + res =3D bank->res; >> + >> + list_for_each_entry(r, &res->children, sibling) { >> + t =3D create_resource("tmp", >> + virt_to_phys((void *)r->start), >> + virt_to_phys((void *)r->end)); >> + list_add_used_region(&t->sibling, &used_regions); >> + } >> + } >> + >> + list_for_each_entry(r, elf_segments, sibling) { >> + t =3D create_resource(r->name, r->start, r->end); >> + list_add_used_region(&t->sibling, &used_regions); >> + } >> + >> + for_each_memory_bank(bank) { >> + resource_size_t start; >> + >> + res =3D bank->res; >> + res =3D create_resource("tmp", >> + virt_to_phys((void *)res->start), >> + virt_to_phys((void *)res->end)); >> + start =3D res->start; >> + >> + list_for_each_entry(r, &used_regions, sibling) { >> + if (res->start > r->end) >> + continue; >> + >> + if (res->end < r->start) >> + continue; >> + >> + if (r->start - start) { >> + struct resource *t; >> + >> + t =3D create_resource("ELF buffer", start, >> + r->start - 1); >> + list_add_used_region(&t->sibling, >> + &elf_relocate_banks); >> + } >> + start =3D r->end + 1; >> + } >> + >> + if (res->end - start) { >> + struct resource *t; >> + >> + t =3D create_resource("ELF buffer", start, res->end); >> + list_add_used_region(&t->sibling, &elf_relocate_banks); >> + } >> + } >> + >> + list_for_each_entry(r, &elf_relocate_banks, sibling) { >> + struct resource *t; >> + >> + t =3D create_resource("ELF buffer", r->start, r->end); >> + list_add_sort(&t->sibling, >> + &elf_relocate_banks_size_sorted, compare); >> + } >> + >> + r =3D list_first_entry(&elf_relocate_banks_size_sorted, struct resou= rce, >> + sibling); >> + >> + return r->start; >> +} >> diff --git a/lib/kexec/kexec.c b/lib/kexec/kexec.c >> new file mode 100644 >> index 000000000..585371c65 >> --- /dev/null >> +++ b/lib/kexec/kexec.c >> @@ -0,0 +1,255 @@ >> +// SPDX-License-Identifier: GPL-2.0+ >> +/* >> + * kexec: Linux boots Linux >> + * >> + * Copyright (C) 2003-2005 Eric Biederman (ebiederm@xmission.com) >> + * Modified (2007-05-15) by Francesco Chiechi to rudely handle mips p= latform >> + * >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +static int sort_segments(struct kexec_info *info) >> +{ >> + int i, j; >> + void *end =3D NULL; >> + >> + /* Do a stupid insertion sort... */ >> + for (i =3D 0; i < info->nr_segments; i++) { >> + int tidx; >> + struct kexec_segment temp; >> + tidx =3D i; >> + for (j =3D i + 1; j < info->nr_segments; j++) { >> + if (info->segment[j].mem < info->segment[tidx].mem) >> + tidx =3D j; >> + } >> + if (tidx !=3D i) { >> + temp =3D info->segment[tidx]; >> + info->segment[tidx] =3D info->segment[i]; >> + info->segment[i] =3D temp; >> + } >> + } >> + >> + /* Now see if any of the segments overlap */ >> + for (i =3D 0; i < info->nr_segments; i++) { >> + if (end > info->segment[i].mem) { >> + pr_err("Overlapping memory segments at %p\n", >> + end); >> + return -EBUSY; >> + } >> + end =3D ((char *)info->segment[i].mem) + info->segment[i].memsz; >> + } >> + >> + return 0; >> +} >> + >> +void add_segment_phys_virt(struct kexec_info *info, >> + const void *buf, size_t bufsz, >> + unsigned long base, size_t memsz, int phys) >> +{ >> + size_t size; >> + int pagesize; >> + >> + if (bufsz > memsz) >> + bufsz =3D memsz; >> + >> + /* Forget empty segments */ >> + if (!memsz) >> + return; >> + >> + /* Round memsz up to a multiple of pagesize */ >> + pagesize =3D 4096; >> + memsz =3D (memsz + (pagesize - 1)) & ~(pagesize - 1); >> + >> + if (phys) >> + base =3D virt_to_phys((void *)base); >> + >> + size =3D (info->nr_segments + 1) * sizeof(info->segment[0]); >> + info->segment =3D xrealloc(info->segment, size); >> + info->segment[info->nr_segments].buf =3D buf; >> + info->segment[info->nr_segments].bufsz =3D bufsz; >> + info->segment[info->nr_segments].mem =3D (void *)base; >> + info->segment[info->nr_segments].memsz =3D memsz; >> + info->nr_segments++; >> + if (info->nr_segments > KEXEC_MAX_SEGMENTS) { >> + pr_warn("Warning: kernel segment limit reached. " >> + "This will likely fail\n"); >> + } >> +} >> + >> +static int kexec_load_one_file(struct kexec_info *info, char *fname) >> +{ >> + char *buf; >> + size_t fsize; >> + int i =3D 0; >> + >> + buf =3D read_file(fname, &fsize); >> + >> + /* FIXME: check buf */ >> + >> + for (i =3D 0; i < kexec_file_types; i++) { >> + if (kexec_file_type[i].probe(buf, fsize) >=3D 0) >> + break; >> + } >> + >> + if (i =3D=3D kexec_file_types) { >> + pr_err("Cannot determine the file type " >> + "of %s\n", fname); >> + return -ENOEXEC; >> + } >> + >> + return kexec_file_type[i].load(buf, fsize, info); >> +} >> + >> +static int kexec_load_binary_file(struct kexec_info *info, char *fnam= e, >> + size_t *fsize, unsigned long base) >> +{ >> + char *buf; >> + >> + buf =3D read_file(fname, fsize); >> + if (!buf) >> + return -ENOENT; >> + >> + add_segment(info, buf, *fsize, base, *fsize); >> + >> + return 0; >> +} >> + >> +static void print_segments(struct kexec_info *info) >> +{ >> + int i; >> + >> + pr_info("print_segments\n"); >> + for (i =3D 0; i < info->nr_segments; i++) { >> + struct kexec_segment *seg =3D &info->segment[i]; >> + >> + pr_info(" %d. buf=3D%#08p bufsz=3D%#lx mem=3D%#08p memsz=3D%#lx\n"= , i, >> + seg->buf, seg->bufsz, seg->mem, seg->memsz); >> + } >> +} >> + >> +static unsigned long find_unused_base(struct kexec_info *info, int *p= added) >> +{ >> + unsigned long base =3D 0; >> + >> + if (info->nr_segments) { >> + int i =3D info->nr_segments - 1; >> + struct kexec_segment *seg =3D &info->segment[i]; >> + >> + base =3D (unsigned long)seg->mem + seg->memsz; >> + } >> + >> + if (!*padded) { >> + /* >> + * Pad it; the kernel scribbles over memory >> + * beyond its load address. >> + * see grub-core/loader/mips/linux.c >> + */ >> + base +=3D 0x100000; >> + *padded =3D 1; >> + } >> + >> + return base; >> +} >> + >> +int kexec_load_bootm_data(struct image_data *data) >> +{ >> + int ret; >> + struct kexec_info info; >> + char *cmdline; >> + const char *t; >> + size_t tlen; >> + size_t fsize; >> + char initrd_cmdline[40]; >> + int padded =3D 0; >> + >> + memset(&info, 0, sizeof(info)); >> + >> + initrd_cmdline[0] =3D 0; >> + >> + ret =3D kexec_load_one_file(&info, data->os_file); >> + if (IS_ERR_VALUE(ret)) { >> + pr_err("Cannot load %s\n", data->os_file); >> + return ret; >> + } >=20 > There is a potential problem here, which I actually hit some time ago. = The=20 > following code places kernel arguments right after os image. This is pe= rfectly=20 > fine in case of vmlinuX. However, if one boots a vmlinuZ image, there i= s no=20 > easily available knowledge of where the decompressed image will reside.= In my=20 > case vmlinux' BSS overlapped with DTB and kernel cmdline segments and w= as=20 > zeroed at linux startup. This was fixed by adding an empty 4k segment a= t 128M,=20 > so further segments were allocated beyond 128M, far enough from the ker= nel=20 > lair: >=20 > + /* FIXME: allocate 4k segment @ 0x8000000 (128M), so further > + * segments will be allocated beyond this address. This preve= nts > + * kernel parameters from being overwritten by the kernel star= tup code. > + */ > + add_segment(&info, (void *)CKSEG0ADDR(0), 4 << 10, 0x8000000, = 4 << 10); >=20 > However this is an ad-hoc solution and, probably, find_unused_base() ma= y take=20 > care of such cases. Yes, correct. This and some other issues would be fixed by porting this part of the code to bootm_load_devicetree() + find_unused_base(). Since my time budget is on the limit, I would prefer to mainline current state of the code ("works for me" TM) and provide platform for testing and cooperation. --=20 Regards, Oleksij --8jEOA3tUZCJieBJNr0yIyWsB9fBl7930B-- --M0gnimUJYoDKDiq8pdZDjg48PRRkEhqKd Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- iQEcBAEBCAAGBQJa/Qp6AAoJEHUDokh1SO+0a14H/0SOuI/Q2YR0FbFczG+hH3t/ nlndK7lg1rfPc/pGwz/C3QLJdkKF7oL+Wr46fOCCHBDa69znXzW81u/gWf6ZA+qL dKIKUXDL2e5UXXzW3YJlQUw70JyWqVFxy0nNK6CgaUN+bJsfECfv7X7WOqhK61vD OXeqv+N/mTfBWvbylrIUz16sjJWyF7gabKn9a8z/bNioc7Af5eK52DGGpwaYUWOi dGILnlbik33kcKJM/F4hxBNF/zZMpVzWTTs6CaCwZ4B5KwKkbUOHwRfplspDXAt9 5EN/NXUExJHK5Z+3J2Uv6/eWsJ0O18zBZg2oYEr9FOkxREIugxpw8ZROkwjKVTA= =3IpG -----END PGP SIGNATURE----- --M0gnimUJYoDKDiq8pdZDjg48PRRkEhqKd-- --===============5417794259126288771== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox --===============5417794259126288771==--