Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/Makefile.common	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -250,5 +250,5 @@
 
 COMPONENTS = \
-	$(KERNEL_PATH)/kernel.bin \
+	$(KERNEL_PATH)/kernel.elf \
 	$(INIT_TASKS) \
 	$(INITRD).img
Index: boot/Makefile.grub
===================================================================
--- boot/Makefile.grub	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/Makefile.grub	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -85,5 +85,5 @@
 	for module in $(MODULES) ; do \
 		echo "	echo 'Loading $$module'" >> $(BOOT_CONFIG) ; \
-		if [ "$$module" = "kernel.bin" ] ; then \
+		if [ "$$module" = "kernel.elf" ] ; then \
 			echo "	$(MULTIBOOT_CMD) /boot/$$module" >> $(BOOT_CONFIG) ; \
 		else \
Index: boot/arch/arm32/Makefile.inc
===================================================================
--- boot/arch/arm32/Makefile.inc	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/arm32/Makefile.inc	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -107,3 +107,4 @@
 	generic/src/gzip.c \
 	generic/src/tar.c \
+	generic/src/kernel.c \
 	generic/src/payload.c
Index: boot/arch/arm32/src/main.c
===================================================================
--- boot/arch/arm32/src/main.c	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/arm32/src/main.c	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -50,4 +50,5 @@
 #include <arch/cp15.h>
 #include <payload.h>
+#include <kernel.h>
 
 static void clean_dcache_poc(void *address, size_t size)
@@ -104,6 +105,8 @@
 	clean_dcache_poc(boot_pt, PTL0_ENTRIES * PTL0_ENTRY_SIZE);
 
+	uintptr_t entry = check_kernel((void *) PA2KA(BOOT_OFFSET));
+
 	printf("Booting the kernel...\n");
-	jump_to_kernel((void *) PA2KA(BOOT_OFFSET), &bootinfo);
+	jump_to_kernel((void *) entry, &bootinfo);
 }
 
Index: boot/arch/ia64/Makefile.inc
===================================================================
--- boot/arch/ia64/Makefile.inc	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/ia64/Makefile.inc	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -61,4 +61,5 @@
 	generic/src/tar.c \
 	generic/src/gzip.c \
+	generic/src/kernel.c \
 	generic/src/payload.c
 
Index: boot/arch/ia64/include/arch/arch.h
===================================================================
--- boot/arch/ia64/include/arch/arch.h	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/ia64/include/arch/arch.h	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -36,4 +36,5 @@
 #define LOADER_ADDRESS  0x4400000
 #define KERNEL_ADDRESS  0x4800000
+#define KERNEL_VADDRESS 0xe000000004800000
 
 #define STACK_SIZE                   8192
Index: boot/arch/ia64/src/main.c
===================================================================
--- boot/arch/ia64/src/main.c	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/ia64/src/main.c	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -44,4 +44,5 @@
 #include <errno.h>
 #include <payload.h>
+#include <kernel.h>
 
 #define DEFAULT_MEMORY_BASE		0x4000000ULL
@@ -182,5 +183,11 @@
 	    (uintptr_t) kernel_start, NULL);
 
-	printf("Booting the kernel ...\n");
-	jump_to_kernel(&bootinfo, kernel_start);
+	uintptr_t entry = check_kernel(kernel_start);
+
+	// FIXME: kernel's entry point is linked at a different address than
+	//        where it is run from.
+	entry = entry - KERNEL_VADDRESS + KERNEL_ADDRESS;
+
+	printf("Booting the kernel at %p...\n", (void *) entry);
+	jump_to_kernel(&bootinfo, (void *) entry);
 }
Index: boot/arch/mips32/Makefile.inc
===================================================================
--- boot/arch/mips32/Makefile.inc	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/mips32/Makefile.inc	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -88,3 +88,4 @@
 	generic/src/gzip.c \
 	generic/src/tar.c \
+	generic/src/kernel.c \
 	generic/src/payload.c
Index: boot/arch/mips32/src/main.c
===================================================================
--- boot/arch/mips32/src/main.c	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/mips32/src/main.c	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -40,4 +40,5 @@
 #include <errno.h>
 #include <payload.h>
+#include <kernel.h>
 
 static bootinfo_t *bootinfo = (bootinfo_t *) PA2KA(BOOTINFO_OFFSET);
@@ -78,5 +79,7 @@
 	}
 
-	printf("Booting the kernel ... \n");
-	jump_to_kernel((void *) PA2KA(BOOT_OFFSET), bootinfo);
+	uintptr_t entry = check_kernel(kernel_start);
+
+	printf("Booting the kernel...\n");
+	jump_to_kernel((void *) entry, bootinfo);
 }
Index: boot/arch/ppc32/Makefile.inc
===================================================================
--- boot/arch/ppc32/Makefile.inc	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/ppc32/Makefile.inc	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -76,3 +76,4 @@
 	generic/src/gzip.c \
 	generic/src/tar.c \
+	generic/src/kernel.c \
 	generic/src/payload.c
Index: boot/arch/ppc32/_link.ld.in
===================================================================
--- boot/arch/ppc32/_link.ld.in	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/ppc32/_link.ld.in	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -6,5 +6,4 @@
 		loader_start = .;
 		*(BOOTSTRAP);
-		*(REALMODE);
 		*(.text);
 	}
Index: boot/arch/ppc32/src/asm.S
===================================================================
--- boot/arch/ppc32/src/asm.S	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/ppc32/src/asm.S	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -152,7 +152,4 @@
 FUNCTION_END(jump_to_kernel)
 
-.section REALMODE, "ax"
-
-.align PAGE_WIDTH
 SYMBOL(real_mode)
 
Index: boot/arch/ppc32/src/main.c
===================================================================
--- boot/arch/ppc32/src/main.c	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/ppc32/src/main.c	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -42,4 +42,5 @@
 #include <errno.h>
 #include <payload.h>
+#include <kernel.h>
 
 #define BALLOC_MAX_SIZE  131072
@@ -68,6 +69,4 @@
 	printf(" %p|%p: real mode trampoline\n", &real_mode, real_mode_pa);
 	printf(" %p|%p: boot info structure\n", &bootinfo, bootinfo_pa);
-	printf(" %p|%p: kernel entry point\n",
-	    (void *) PA2KA(BOOT_OFFSET), (void *) BOOT_OFFSET);
 	printf(" %p|%p: loader entry point\n",
 	    (void *) LOADER_ADDRESS, loader_address_pa);
@@ -144,6 +143,7 @@
 	}
 
+	uintptr_t entry = check_kernel_translated(inflate_base, 0);
+
 	printf("Booting the kernel...\n");
-	jump_to_kernel(bootinfo_pa, transtable_pa, pages, real_mode_pa,
-	    PA2KA(BOOT_OFFSET));
+	jump_to_kernel(bootinfo_pa, transtable_pa, pages, real_mode_pa, entry);
 }
Index: boot/arch/riscv64/Makefile.inc
===================================================================
--- boot/arch/riscv64/Makefile.inc	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/riscv64/Makefile.inc	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -30,5 +30,4 @@
 BFD_OUTPUT = $(BFD_NAME)
 BFD_ARCH = riscv
-BFD = binary
 
 BITS = 64
@@ -51,3 +50,4 @@
 	generic/src/gzip.c \
 	generic/src/tar.c \
+	generic/src/kernel.c \
 	generic/src/payload.c
Index: boot/arch/riscv64/include/arch/asm.h
===================================================================
--- boot/arch/riscv64/include/arch/asm.h	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/riscv64/include/arch/asm.h	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -36,5 +36,5 @@
 extern char pt_page[];
 
-extern _Noreturn void jump_to_kernel(uintptr_t);
+extern _Noreturn void jump_to_kernel(uintptr_t, uintptr_t);
 
 #endif
Index: boot/arch/riscv64/src/asm.S
===================================================================
--- boot/arch/riscv64/src/asm.S	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/riscv64/src/asm.S	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -125,5 +125,6 @@
 	csrw mstatus, t0
 
-	li ra, PA2KA(BOOT_OFFSET)
+	/* Entry point address is in a1. */
+	mv ra, a1
 	csrw mepc, ra
 
Index: boot/arch/riscv64/src/main.c
===================================================================
--- boot/arch/riscv64/src/main.c	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/riscv64/src/main.c	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -42,4 +42,5 @@
 #include <halt.h>
 #include <payload.h>
+#include <kernel.h>
 
 static bootinfo_t bootinfo;
@@ -88,5 +89,7 @@
 	extract_payload(&bootinfo.taskmap, load_addr, end, kernel_addr, NULL);
 
+	uintptr_t entry = check_kernel(load_addr);
+
 	printf("Booting the kernel...\n");
-	jump_to_kernel(PA2KA(&bootinfo));
+	jump_to_kernel(PA2KA(&bootinfo), entry);
 }
Index: boot/arch/sparc64/Makefile.inc
===================================================================
--- boot/arch/sparc64/Makefile.inc	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/sparc64/Makefile.inc	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -72,3 +72,4 @@
 	generic/src/gzip.c \
 	generic/src/tar.c \
+	generic/src/kernel.c \
 	generic/src/payload.c
Index: boot/arch/sparc64/src/main.c
===================================================================
--- boot/arch/sparc64/src/main.c	(revision d59718e246e2f30ad97e0364fac7e75455fcb7d1)
+++ boot/arch/sparc64/src/main.c	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -43,4 +43,5 @@
 #include <errno.h>
 #include <payload.h>
+#include <kernel.h>
 
 /* The lowest ID (read from the VER register) of some US3 CPU model */
@@ -257,6 +258,8 @@
 		sun4u_smp();
 
+	uintptr_t entry = check_kernel((void *) KERNEL_ADDRESS);
+
 	printf("Booting the kernel ...\n");
-	jump_to_kernel(bootinfo.physmem_start | BSP_PROCESSOR, &bootinfo, subarch,
-	    (void *) KERNEL_ADDRESS);
-}
+	jump_to_kernel(bootinfo.physmem_start | BSP_PROCESSOR, &bootinfo,
+	    subarch, (void *) entry);
+}
Index: boot/generic/include/kernel.h
===================================================================
--- boot/generic/include/kernel.h	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
+++ boot/generic/include/kernel.h	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018 Jiří Zárevúcky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BOOT_ELF_H_
+#define BOOT_ELF_H_
+
+#include <stdint.h>
+
+uintptr_t check_kernel_translated(void *, uintptr_t);
+uintptr_t check_kernel(void *);
+
+#endif
Index: boot/generic/src/kernel.c
===================================================================
--- boot/generic/src/kernel.c	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
+++ boot/generic/src/kernel.c	(revision bf05c7451aebfdd5f9d9f9cba5cc3d3190368378)
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2018 Jiří Zárevúcky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <abi/elf.h>
+#include <halt.h>
+#include <printf.h>
+#include <kernel.h>
+#include <stdbool.h>
+
+// FIXME: elf_is_valid is a duplicate of the same-named libc function.
+
+// TODO: better kernel ELF loading
+//
+// Currently the boot loader is very primitive. It loads the ELF file as
+// a contiguous span starting at a predefined offset, and then checks load
+// segments in it to make sure they are correctly positioned. Ideally, this
+// should change to a more flexible loader that actually loads based on the
+// kernel's ELF segments. There would still be some restrictions however.
+// ELF vaddr and paddr fields offer some flexibility in their interpretation,
+// so I propose the following scheme, to correctly express everything various
+// architectures require:
+//
+//     - in vaddr and paddr fields, addresses numerically in the lower half are
+//       interpreted as physical addresses, addresses in the upper half are
+//       interpreted as virtual addresses.
+//
+//     - If vaddr is a virtual address, the segment is mapped into the kernel's
+//       virtual address space at vaddr.
+//
+//     - If vaddr is a physical address, it must be the same as paddr.
+//       Loader loads the segment at the given physical address, but does not
+//       map it into the kernel's virtual address space. Symbols defined in this
+//       segment are only accessible with paging disabled.
+//
+//     - If paddr is a physical address, the loader must load the segment at
+//       physical address paddr, or die trying.
+//
+//     - If paddr is a virtual address, it must be the same as vaddr.
+//       Loader may allocate the physical location arbitrarily.
+//
+//     - If the kernel is a Position Independent Executable, all this is
+//       irrelevant, paddr must be the same as vaddr, vaddr is always the
+//       virtual address offset, and loader can choose the virtual address
+//       base arbitrarily within some predefined constraints. We might want
+//       to support PIE kernel on architectures that need some code at fixed
+//       physical address. In that case, the "real mode" code should probably
+//       be in a separate ELF file from the rest of kernel.
+//
+
+/**
+ * Checks that the ELF header is valid for the running system.
+ */
+static bool elf_is_valid(const elf_header_t *header)
+{
+	// TODO: check more things
+	// TODO: debug output
+
+	if (header->e_ident[EI_MAG0] != ELFMAG0 ||
+	    header->e_ident[EI_MAG1] != ELFMAG1 ||
+	    header->e_ident[EI_MAG2] != ELFMAG2 ||
+	    header->e_ident[EI_MAG3] != ELFMAG3) {
+		return false;
+	}
+
+	if (header->e_ident[EI_DATA] != ELF_DATA_ENCODING ||
+	    header->e_machine != ELF_MACHINE ||
+	    header->e_ident[EI_VERSION] != EV_CURRENT ||
+	    header->e_version != EV_CURRENT ||
+	    header->e_ident[EI_CLASS] != ELF_CLASS) {
+		return false;
+	}
+
+	if (header->e_phentsize != sizeof(elf_segment_header_t)) {
+		return false;
+	}
+
+	if (header->e_type != ET_EXEC && header->e_type != ET_DYN) {
+		return false;
+	}
+
+	if (header->e_phoff == 0) {
+		return false;
+	}
+
+	return true;
+}
+
+uintptr_t check_kernel(void *start)
+{
+	return check_kernel_translated(start, (uintptr_t) start);
+}
+
+/**
+ * Checks that the kernel ELF image is valid, and returns the entry point
+ * address. We check that the image's load addresses match the actual location.
+ *
+ * @param start  Pointer to the start of the ELF file in memory.
+ * @param actual_addr  Start address where the kernel is moved after the check
+ *                     but before it is executed.
+ *
+ * @return  Entry point address in *kernel's* address space.
+ */
+uintptr_t check_kernel_translated(void *start, uintptr_t actual_addr)
+{
+	elf_header_t *header = (elf_header_t *) start;
+
+	if (!elf_is_valid(header)) {
+		printf("Kernel is not a valid ELF image.\n");
+		halt();
+	}
+
+	elf_segment_header_t *phdr =
+	    (elf_segment_header_t *) ((uintptr_t) start + header->e_phoff);
+
+	/* Walk through PT_LOAD headers, to find out the size of the module. */
+	for (int i = 0; i < header->e_phnum; i++) {
+		if (phdr[i].p_type != PT_LOAD)
+			continue;
+
+		uintptr_t expected = actual_addr + phdr[i].p_offset;
+		uintptr_t got = phdr[i].p_paddr;
+		if (expected != got) {
+			printf("Incorrect kernel load address. "
+			    "Expected: %p, got %p\n",
+			    (void *) expected, (void *) got);
+			halt();
+		}
+
+		if (phdr[i].p_filesz != phdr[i].p_memsz) {
+			printf("Kernel's memory size is greater than its file"
+			    " size. We don't currently support that.\n");
+			halt();
+		}
+	}
+
+	return (uintptr_t) header->e_entry;
+}
