source: mainline/boot/generic/src/kernel.c@ 39916d6

Last change on this file since 39916d6 was d7f7a4a, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 years ago

Replace some license headers with SPDX identifier

Headers are replaced using tools/transorm-copyright.sh only
when it can be matched verbatim with the license header used
throughout most of the codebase.

  • Property mode set to 100644
File size: 4.5 KB
Line 
1/*
2 * SPDX-FileCopyrightText: 2018 Jiří Zárevúcky
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <abi/elf.h>
8#include <halt.h>
9#include <printf.h>
10#include <kernel.h>
11#include <stdbool.h>
12
13// FIXME: elf_is_valid is a duplicate of the same-named libc function.
14
15// TODO: better kernel ELF loading
16//
17// Currently the boot loader is very primitive. It loads the ELF file as
18// a contiguous span starting at a predefined offset, and then checks load
19// segments in it to make sure they are correctly positioned. Ideally, this
20// should change to a more flexible loader that actually loads based on the
21// kernel's ELF segments. There would still be some restrictions however.
22// ELF vaddr and paddr fields offer some flexibility in their interpretation,
23// so I propose the following scheme, to correctly express everything various
24// architectures require:
25//
26// - in vaddr and paddr fields, addresses numerically in the lower half are
27// interpreted as physical addresses, addresses in the upper half are
28// interpreted as virtual addresses.
29//
30// - If vaddr is a virtual address, the segment is mapped into the kernel's
31// virtual address space at vaddr.
32//
33// - If vaddr is a physical address, it must be the same as paddr.
34// Loader loads the segment at the given physical address, but does not
35// map it into the kernel's virtual address space. Symbols defined in this
36// segment are only accessible with paging disabled.
37//
38// - If paddr is a physical address, the loader must load the segment at
39// physical address paddr, or die trying.
40//
41// - If paddr is a virtual address, it must be the same as vaddr.
42// Loader may allocate the physical location arbitrarily.
43//
44// - If the kernel is a Position Independent Executable, all this is
45// irrelevant, paddr must be the same as vaddr, vaddr is always the
46// virtual address offset, and loader can choose the virtual address
47// base arbitrarily within some predefined constraints. We might want
48// to support PIE kernel on architectures that need some code at fixed
49// physical address. In that case, the "real mode" code should probably
50// be in a separate ELF file from the rest of kernel.
51//
52
53/**
54 * Checks that the ELF header is valid for the running system.
55 */
56static bool elf_is_valid(const elf_header_t *header)
57{
58 // TODO: check more things
59 // TODO: debug output
60
61 if (header->e_ident[EI_MAG0] != ELFMAG0 ||
62 header->e_ident[EI_MAG1] != ELFMAG1 ||
63 header->e_ident[EI_MAG2] != ELFMAG2 ||
64 header->e_ident[EI_MAG3] != ELFMAG3) {
65 return false;
66 }
67
68 if (header->e_ident[EI_DATA] != ELF_DATA_ENCODING ||
69 header->e_machine != ELF_MACHINE ||
70 header->e_ident[EI_VERSION] != EV_CURRENT ||
71 header->e_version != EV_CURRENT ||
72 header->e_ident[EI_CLASS] != ELF_CLASS) {
73 return false;
74 }
75
76 if (header->e_phentsize != sizeof(elf_segment_header_t)) {
77 return false;
78 }
79
80 if (header->e_type != ET_EXEC && header->e_type != ET_DYN) {
81 return false;
82 }
83
84 if (header->e_phoff == 0) {
85 return false;
86 }
87
88 return true;
89}
90
91uintptr_t check_kernel(void *start)
92{
93 return check_kernel_translated(start, (uintptr_t) start);
94}
95
96/**
97 * Checks that the kernel ELF image is valid, and returns the entry point
98 * address. We check that the image's load addresses match the actual location.
99 *
100 * @param start Pointer to the start of the ELF file in memory.
101 * @param actual_addr Start address where the kernel is moved after the check
102 * but before it is executed.
103 *
104 * @return Entry point address in *kernel's* address space.
105 */
106uintptr_t check_kernel_translated(void *start, uintptr_t actual_addr)
107{
108 elf_header_t *header = (elf_header_t *) start;
109
110 if (!elf_is_valid(header)) {
111 printf("Kernel is not a valid ELF image.\n");
112 halt();
113 }
114
115 elf_segment_header_t *phdr =
116 (elf_segment_header_t *) ((uintptr_t) start + header->e_phoff);
117
118 /* Walk through PT_LOAD headers, to find out the size of the module. */
119 for (int i = 0; i < header->e_phnum; i++) {
120 if (phdr[i].p_type != PT_LOAD)
121 continue;
122
123 uintptr_t expected = actual_addr + phdr[i].p_offset;
124 uintptr_t got = phdr[i].p_paddr;
125 if (expected != got) {
126 printf("Incorrect kernel load address. "
127 "Expected: %p, got %p\n",
128 (void *) expected, (void *) got);
129 halt();
130 }
131
132 if (phdr[i].p_filesz != phdr[i].p_memsz) {
133 printf("Kernel's memory size is greater than its file"
134 " size. We don't currently support that.\n");
135 halt();
136 }
137 }
138
139 return (uintptr_t) header->e_entry;
140}
Note: See TracBrowser for help on using the repository browser.