summaryrefslogtreecommitdiff
path: root/kexec/arch/arm64/kexec-elf-arm64.c
blob: 900c6939ab62d02c35ef4d958847e9d32d8a296b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * ARM64 kexec elf support.
 */

#define _GNU_SOURCE

#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <linux/elf.h>

#include "kexec-arm64.h"
#include "kexec-elf.h"
#include "kexec-syscall.h"

int elf_arm64_probe(const char *kernel_buf, off_t kernel_size)
{
	struct mem_ehdr ehdr;
	int result;

	result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);

	if (result < 0) {
		dbgprintf("%s: Not an ELF executable.\n", __func__);
		goto on_exit;
	}

	if (ehdr.e_machine != EM_AARCH64) {
		dbgprintf("%s: Not an AARCH64 ELF executable.\n", __func__);
		result = -1;
		goto on_exit;
	}

	result = 0;
on_exit:
	free_elf_info(&ehdr);
	return result;
}

int elf_arm64_load(int argc, char **argv, const char *kernel_buf,
	off_t kernel_size, struct kexec_info *info)
{
	const struct arm64_image_header *header = NULL;
	unsigned long kernel_segment;
	struct mem_ehdr ehdr;
	int result;
	int i;

	if (info->kexec_flags & KEXEC_ON_CRASH) {
		fprintf(stderr, "kexec: kdump not yet supported on arm64\n");
		return EFAILED;
	}

	result = build_elf_exec_info(kernel_buf, kernel_size, &ehdr, 0);

	if (result < 0) {
		dbgprintf("%s: build_elf_exec_info failed\n", __func__);
		goto exit;
	}

	/* Find and process the arm64 image header. */

	for (i = 0; i < ehdr.e_phnum; i++) {
		struct mem_phdr *phdr = &ehdr.e_phdr[i];
		unsigned long header_offset;

		if (phdr->p_type != PT_LOAD)
			continue;

		/*
		 * When CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET=y the image header
		 * could be offset in the elf segment.  The linker script sets
		 * ehdr.e_entry to the start of text.
		 */

		header_offset = ehdr.e_entry - phdr->p_vaddr;

		header = (const struct arm64_image_header *)(
			kernel_buf + phdr->p_offset + header_offset);

		if (!arm64_process_image_header(header)) {
			dbgprintf("%s: e_entry:        %016llx\n", __func__,
				ehdr.e_entry);
			dbgprintf("%s: p_vaddr:        %016llx\n", __func__,
				phdr->p_vaddr);
			dbgprintf("%s: header_offset:  %016lx\n", __func__,
				header_offset);

			break;
		}
	}

	if (i == ehdr.e_phnum) {
		dbgprintf("%s: Valid arm64 header not found\n", __func__);
		result = EFAILED;
		goto exit;
	}

	kernel_segment = arm64_locate_kernel_segment(info);

	if (kernel_segment == ULONG_MAX) {
		dbgprintf("%s: Kernel segment is not allocated\n", __func__);
		result = EFAILED;
		goto exit;
	}

	arm64_mem.vp_offset = _ALIGN_DOWN(ehdr.e_entry, MiB(2));
	arm64_mem.vp_offset -= kernel_segment - get_phys_offset();

	dbgprintf("%s: kernel_segment: %016lx\n", __func__, kernel_segment);
	dbgprintf("%s: text_offset:    %016lx\n", __func__,
		arm64_mem.text_offset);
	dbgprintf("%s: image_size:     %016lx\n", __func__,
		arm64_mem.image_size);
	dbgprintf("%s: phys_offset:    %016lx\n", __func__,
		arm64_mem.phys_offset);
	dbgprintf("%s: vp_offset:      %016lx\n", __func__,
		arm64_mem.vp_offset);
	dbgprintf("%s: PE format:      %s\n", __func__,
		(arm64_header_check_pe_sig(header) ? "yes" : "no"));

	/* create and initialize elf core header segment */
	if (info->kexec_flags & KEXEC_ON_CRASH) {
		result = load_crashdump_segments(info);
		if (result) {
			dbgprintf("%s: Creating eflcorehdr failed.\n",
								__func__);
			goto exit;
		}
	}

	/* load the kernel */
	result = elf_exec_load(&ehdr, info);

	if (result) {
		dbgprintf("%s: elf_exec_load failed\n", __func__);
		goto exit;
	}

	/* load additional data */
	result = arm64_load_other_segments(info, kernel_segment
		+ arm64_mem.text_offset);

exit:
	reset_vp_offset();
	free_elf_info(&ehdr);
	if (result)
		fprintf(stderr, "kexec: Bad elf image file, load failed.\n");
	return result;
}

void elf_arm64_usage(void)
{
	printf(
"     An ARM64 ELF image, big or little endian.\n"
"     Typically vmlinux or a stripped version of vmlinux.\n\n");
}