source: mainline/boot/generic/src/kernel.c@ 645d3832

Last change on this file since 645d3832 was cfdeedc, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Keep kernel in ELF format

By keeping kernel in an ELF file (instead of converting it to
a flat binary), we can use the information it contains, like
symbol table and debug info.

We can also later implement more advanced functionality, like
loading kernel at multiple discontiguous blocks, or loading
a position-independent kernel at a randomized address.

Currently the functionality is quite restricted, to keep changes
to a minimum. Code in boot/generic/src/kernel.c validates that
the kernel image was built with the same addresses as the boot
loader uses, giving an extra level of sanity checking compared
to a flat binary.

  • Property mode set to 100644
File size: 5.8 KB
RevLine 
[cfdeedc]1/*
2 * Copyright (c) 2018 Jiří Zárevúcky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <abi/elf.h>
30#include <halt.h>
31#include <printf.h>
32#include <kernel.h>
33#include <stdbool.h>
34
35// FIXME: elf_is_valid is a duplicate of the same-named libc function.
36
37// TODO: better kernel ELF loading
38//
39// Currently the boot loader is very primitive. It loads the ELF file as
40// a contiguous span starting at a predefined offset, and then checks load
41// segments in it to make sure they are correctly positioned. Ideally, this
42// should change to a more flexible loader that actually loads based on the
43// kernel's ELF segments. There would still be some restrictions however.
44// ELF vaddr and paddr fields offer some flexibility in their interpretation,
45// so I propose the following scheme, to correctly express everything various
46// architectures require:
47//
48// - in vaddr and paddr fields, addresses numerically in the lower half are
49// interpreted as physical addresses, addresses in the upper half are
50// interpreted as virtual addresses.
51//
52// - If vaddr is a virtual address, the segment is mapped into the kernel's
53// virtual address space at vaddr.
54//
55// - If vaddr is a physical address, it must be the same as paddr.
56// Loader loads the segment at the given physical address, but does not
57// map it into the kernel's virtual address space. Symbols defined in this
58// segment are only accessible with paging disabled.
59//
60// - If paddr is a physical address, the loader must load the segment at
61// physical address paddr, or die trying.
62//
63// - If paddr is a virtual address, it must be the same as vaddr.
64// Loader may allocate the physical location arbitrarily.
65//
66// - If the kernel is a Position Independent Executable, all this is
67// irrelevant, paddr must be the same as vaddr, vaddr is always the
68// virtual address offset, and loader can choose the virtual address
69// base arbitrarily within some predefined constraints. We might want
70// to support PIE kernel on architectures that need some code at fixed
71// physical address. In that case, the "real mode" code should probably
72// be in a separate ELF file from the rest of kernel.
73//
74
75/**
76 * Checks that the ELF header is valid for the running system.
77 */
78static bool elf_is_valid(const elf_header_t *header)
79{
80 // TODO: check more things
81 // TODO: debug output
82
83 if (header->e_ident[EI_MAG0] != ELFMAG0 ||
84 header->e_ident[EI_MAG1] != ELFMAG1 ||
85 header->e_ident[EI_MAG2] != ELFMAG2 ||
86 header->e_ident[EI_MAG3] != ELFMAG3) {
87 return false;
88 }
89
90 if (header->e_ident[EI_DATA] != ELF_DATA_ENCODING ||
91 header->e_machine != ELF_MACHINE ||
92 header->e_ident[EI_VERSION] != EV_CURRENT ||
93 header->e_version != EV_CURRENT ||
94 header->e_ident[EI_CLASS] != ELF_CLASS) {
95 return false;
96 }
97
98 if (header->e_phentsize != sizeof(elf_segment_header_t)) {
99 return false;
100 }
101
102 if (header->e_type != ET_EXEC && header->e_type != ET_DYN) {
103 return false;
104 }
105
106 if (header->e_phoff == 0) {
107 return false;
108 }
109
110 return true;
111}
112
113uintptr_t check_kernel(void *start)
114{
115 return check_kernel_translated(start, (uintptr_t) start);
116}
117
118/**
119 * Checks that the kernel ELF image is valid, and returns the entry point
120 * address. We check that the image's load addresses match the actual location.
121 *
122 * @param start Pointer to the start of the ELF file in memory.
123 * @param actual_addr Start address where the kernel is moved after the check
124 * but before it is executed.
125 *
126 * @return Entry point address in *kernel's* address space.
127 */
128uintptr_t check_kernel_translated(void *start, uintptr_t actual_addr)
129{
130 elf_header_t *header = (elf_header_t *) start;
131
132 if (!elf_is_valid(header)) {
133 printf("Kernel is not a valid ELF image.\n");
134 halt();
135 }
136
137 elf_segment_header_t *phdr =
138 (elf_segment_header_t *) ((uintptr_t) start + header->e_phoff);
139
140 /* Walk through PT_LOAD headers, to find out the size of the module. */
141 for (int i = 0; i < header->e_phnum; i++) {
142 if (phdr[i].p_type != PT_LOAD)
143 continue;
144
145 uintptr_t expected = actual_addr + phdr[i].p_offset;
146 uintptr_t got = phdr[i].p_paddr;
147 if (expected != got) {
148 printf("Incorrect kernel load address. "
149 "Expected: %p, got %p\n",
150 (void *) expected, (void *) got);
151 halt();
152 }
153
154 if (phdr[i].p_filesz != phdr[i].p_memsz) {
155 printf("Kernel's memory size is greater than its file"
156 " size. We don't currently support that.\n");
157 halt();
158 }
159 }
160
161 return (uintptr_t) header->e_entry;
162}
Note: See TracBrowser for help on using the repository browser.